Şeffaf karşılaştırıcılar nedir?


106

C ++ 14'te, ilişkilendirilebilir kapsayıcılar C ++ 11'den değişmiş gibi görünüyor - [Associative.reqmts] / 13 diyor ki:

Üye işlev şablonları find, count, lower_bound, upper_bound, ve equal_rangetip sürece aşırı yük çözünürlükte katılamazlar Compare::is_transparentbulunmaktadır.

Bir karşılaştırıcıyı "şeffaf" yapmanın amacı nedir?

C ++ 14 ayrıca aşağıdaki gibi kitaplık şablonları sağlar:

template <class T = void> struct less {
    constexpr bool operator()(const T& x, const T& y) const;
    typedef T first_argument_type;
    typedef T second_argument_type;
    typedef bool result_type;
};

template <> struct less<void> {
    template <class T, class U> auto operator()(T&& t, U&& u) const
    -> decltype(std::forward<T>(t) < std::forward<U>(u));
    typedef *unspecified* is_transparent;
};

Yani, örneğin, std::set<T, std::less<T>>olur değil şeffaf bir karşılaştırıcı var, ama std::set<T, std::less<>> olur bir tane var.

Bu hangi sorunu çözer ve bu standart konteynerlerin çalışma şeklini değiştirir mi? Örneğin, şablon parametreleri std::sethala Key, Compare = std::less<Key>, ...bu yüzden varsayılan ayar onun kaybetmek yok, find, countvb üyeleri?


Örneğin, bu cppreference açıklamasına bakın . Ve şimdi kendimi aptal gibi hissediyorum, çünkü "üye işlevi şablonu " kelimesini
görüyorum


cppreference ayrıca tanıtıcı bir sahiptir en.cppreference.com/w/cpp/utility/functional/less_void
Cubbi

Yanıtlar:


60

Bu hangi sorunu çözer?

Bkz DIETMAR yanıtını ve remyabel cevabını .

ve bu standart konteynerlerin çalışma şeklini değiştiriyor mu?

Hayır, varsayılan olarak değil.

Yeni üye işlev şablonu aşırı yüklemeleri findvb., Anahtar türünün kendisini kullanmak yerine kapsayıcının anahtarıyla karşılaştırılabilir bir tür kullanmanıza izin verir. Gerekçe ve bu özelliği eklemek için ayrıntılı, dikkatlice yazılmış bir teklif için Joaquín Mª López Muñoz tarafından hazırlanan N3465'e bakın .

Bristol toplantısında LWG, heterojen arama özelliğinin yararlı ve arzu edilir olduğu konusunda hemfikirdi, ancak Joaquin'in teklifinin her durumda güvenli olacağından emin olamadık. N3465 önerisi bazı programlar için ciddi sorunlara neden olabilirdi ( Mevcut koda etkisi bölümüne bakın). Joaquín, LWG'nin artılarını ve eksilerini anlamasına yardımcı olmak için çok yararlı olan, farklı ödünleşimlere sahip bazı alternatif uygulamalarla güncellenmiş bir taslak teklif hazırladı, ancak hepsi bazı programları bir şekilde kırma riskini aldılar, bu nedenle özelliği eklemek için bir fikir birliği yoktu. Özelliği koşulsuz olarak eklemenin güvenli olmamasına rağmen, varsayılan olarak devre dışı bırakılmasının ve yalnızca "etkinleştirilmesinin" güvenli olacağına karar verdik.

N3657 teklifinin temel farkı ( benim ve N3465'e dayanan STL'nin son dakika revizyonu ve Joaquín tarafından daha sonra yayınlanmamış bir taslaktı), is_transparentyeni işlevselliği seçmek için kullanılabilecek protokol olarak türü eklemekti .

Bir "şeffaf işlevci" (yani bir is_transparenttürü tanımlayan ) kullanmazsanız, kaplar her zaman yaptıkları gibi davranır ve bu hala varsayılan değerdir.

Eğer kullanım tercih IFF std::less<>(14 C ++ için yeni olan) ya da başka bir "şeffaf funktor" o zaman yeni işlevi olsun yazın.

std::less<>Takma ad şablonlarıyla kullanmak kolaydır:

template<typename T, typename Cmp = std::less<>, typename Alloc = std::allocator<T>>
  using set = std::set<T, Cmp, Alloc>;

İsim , C ++ 14'e "elmas operatörlerini" ekleyen STL'nin N3421'denis_transparent geliyor . "Şeffaf bir işlev", herhangi bir bağımsız değişken türünü (aynı olması gerekmez) kabul eden ve bu bağımsız değişkenleri başka bir operatöre ileten bir işlevdir. Böyle bir işlev, ilişkisel kaplarda heterojen arama için tam olarak istediğiniz şey olur, bu nedenle tür , tüm elmas işleçlerine eklendi ve yeni işlevselliğin ilişkili kaplarda etkinleştirilmesi gerektiğini belirtmek için etiket türü olarak kullanıldı. Teknik olarak, kapsayıcıların "şeffaf bir işleve" ihtiyacı yoktur, yalnızca onu heterojen türlerle çağırmayı destekleyen biri (örneğin, https://stackoverflow.com/a/18940595/981959'daki tür STL'nin tanımına göre şeffaf değildir,is_transparentpointer_comppointer_comp::is_transparentsorunu çözmek için kullanılmasına izin verir). Eğer sadece hiç Gözlerinde farklı arama ederse std::set<T, C>Çeşidi tuşlarıyla Tveya intsonra Csadece Çeşidi argümanlarla çağrılabilir olması gerekir Tve int(her iki sırada), gerçekten şeffaf olması gerekmez. Bu adı kısmen daha iyi bir isim bulamadığımız için kullandık (Bu is_polymorphictür işlevler statik polimorfizm kullandığı için tercih ederdim , ancak zaten std::is_polymorphicdinamik polimorfizmi ifade eden bir tür özelliği var).


3
Hey, Woolstar'ın bağlantılı konuşmasında STL'nin "Elbette kafanda şablon argüman çıkarımı yapabilirsin" dediği kişi sen miydin?
Kerrek SB

10
Hayır, orada değildim, ancak kafalarında benden çok daha uyumlu derleyiciler olan insanlar var :)
Jonathan Wakely

Sanırım "elmas operatörü" <>bağlantılı teklife atıfta bulunuyor , ancak bu teklif sunulmadı <>- boş bir şablon parametre listesi için mevcut sözdizimi. "Elmas operatör işlevleri" biraz daha az kafa karıştırıcı olacaktır.
Qwertie

33

C ++ 11 yılında üye şablonlar var olmayan find(), lower_bound()vb olduğunu Yani, hiçbir şey bu değişiklikten kaybolur. Üye şablonları, heterojen anahtarların ilişkisel kapsayıcılarla kullanılmasına izin vermek için n3657 ile tanıtıldı. İyi ve kötü örnek dışında bunun yararlı olduğu somut bir örnek görmüyorum!

is_transparentKullanım istenmeyen dönüşümleri önlemek amaçlanmaktadır. Üye şablonları kısıtlanmamışsa, mevcut kod, üye şablonları olmadan dönüştürülecek olan nesnelerden doğrudan geçebilir. N3657'deki örnek kullanım durumu, std::set<std::string>bir dize değişmezi kullanarak bir nesneyi bulmaktır : C ++ 11 tanımıyla std::string, bir dize değişmezlerini karşılık gelen üye işlevine iletirken bir nesne oluşturulur. Değişiklikle dize değişmezini doğrudan kullanmak mümkündür. Temel karşılaştırma işlevi nesnesi özel olarak bu bağlamda std::stringuygulanırsa kötüdür çünkü şimdi std::stringher karşılaştırma için bir oluşturulacaktır. Öte yandan, temeldeki karşılaştırma işlevi nesnesi birstd::string ve geçici bir nesnenin oluşturulmasını engelleyebilecek bir dizi değişmezi.

is_transparentKarşılaştırma işlevi nesnesindeki yuvalanmış tür, şablonlu üye işlevinin kullanılması gerekip gerekmediğini belirtmek için bir yol sağlar: karşılaştırma işlevi nesnesi heterojen bağımsız değişkenlerle başa çıkabiliyorsa, bu türü farklı bağımsız değişkenlerle verimli bir şekilde başa çıkabileceğini belirtmek için tanımlar. Örneğin, yeni operatör işlevi nesneleri yalnızca yetki verir operator<()ve şeffaf olduklarını iddia eder. Bu, en azından, std::stringoperatörlerin char const*argüman olarak almasından daha az aşırı yüklenen çalışır . Bu işlev nesneleri de yeni olduklarından, yanlış bir şey yapsalar bile (yani bir tür için bir dönüştürme gerektirseler bile), en azından, performans düşüşüyle ​​sonuçlanan sessiz bir değişiklik olmayacaktır.


Teşekkürler - diğer soru hakkındaki yorumuma bakın: Varsayılan olarak şeffaf davranışı alıyor musunuz?
Kerrek SB

8
@KerrekSB: Saydam davranış, is_transparentkarşılaştırma işlevi nesnesinde 23.2.4 [Associative.reqmts] paragraf 13'e göre tanımlandığında etkinleştirilir . Varsayılan karşılaştırma işlevi nesneleri std::less<Key>23.4.2 [Associative.map.syn] ve 23.4'e göredir . 3 [ilişkilendirici.set.syn]. 20.10.5 [Karşılaştırma] paragraf 4, genel şablon göre için std::less<...>etmez olmayan bir iç içe tipini tanımlamak is_transparentancak std::less<void>uzmanlık yapar. Yani, hayır, varsayılan olarak şeffaf bir operatör almazsınız.
Dietmar Kühl 13

İsimlendirme konusunda herhangi bir fikriniz var mı? Yani neden is_transparent?
plasmacel

"Bunun yararlı olduğu somut bir örnek" mi istiyorsunuz? İşte benim kullanım
durumum

19

Aşağıdakiler n3657'den tüm kopya-makarnadır .

S. Bir karşılaştırıcıyı "şeffaf" yapmanın amacı nedir?

A. İlişkilendirilebilir kapsayıcı arama işlevleri (bul, alt_bound, üst_bound, eşit_aralık) yalnızca anahtar_türü bağımsız değişkenini alır ve kullanıcıların aramayı yapmak için anahtar_türü nesnesini (örtük veya açıkça) oluşturmasını gerektirir. Bu pahalı olabilir, örneğin, karşılaştırma işlevi nesnenin yalnızca bir alanına baktığında bir kümede aramak için büyük bir nesne oluşturmak. Kullanıcılar arasında, anahtar_türü ile karşılaştırılabilen diğer türleri kullanarak arama yapabilmek için güçlü bir istek vardır.

S. Bu hangi sorunu çözer?

C. LWG'nin kodla ilgili aşağıdaki gibi endişeleri vardı:

std::set<std::string> s = /* ... */;
s.find("key");

C ++ 11'de bu, geçici olarak tek bir std :: string oluşturacak ve ardından anahtarı bulmak için bunu öğelerle karşılaştıracaktır.

N3465 tarafından önerilen değişiklikle std :: set :: find () işlevi, const char * 'ı karşılaştırıcı işlevi olan std :: less'a geçirecek ve bunun için geçici bir std :: string oluşturacak olan kısıtlanmamış bir şablon olacaktır. her karşılaştırma. LWG, bu performans sorununu ciddi bir sorun olarak değerlendirdi. Şablon find () işlevi ayrıca işaretçilerden oluşan bir konteynerde NULL bulunmasını engeller, bu da daha önce geçerli olan kodun artık derlenmemesine neden olur, ancak bu, sessiz performans regresyonundan daha az ciddi bir sorun olarak görülmüştür.

S: Bu, standart konteynerlerin çalışma şeklini değiştirir mi?

A. Bu öneri, arama üye işlevlerini üye işlev şablonlarıyla aşırı yükleyerek ilişkili kapsayıcıları değiştirir. Dil değişikliği yok.

S: varsayılan set de bul, say, vb. Üyelerini kaybeder.

A. Karşılaştırma işlevleri olarak yeni C ++ 14 kitaplık özellikleri kullanılmadıkça üye işlevler mevcut olmadığından, hemen hemen tüm mevcut C ++ 11 kodu etkilenmez.

Yakk'tan alıntı yapmak için ,

C ++ 14'te, std :: set :: find, Compare :: is_transparent mevcutsa bir şablon işlevidir. Geçtiğiniz türün Anahtar olması gerekmez, yalnızca karşılaştırıcınız ile eşdeğerdir.

ve n3657,

23.2.4 [ilişkilendirici.reqmts] 'de 13. paragrafı ekleyin: Üye işlev şablonları find, lower_bound, upper_bound ve equal_range, Compare :: is_transparent tipi mevcut olmadığı sürece aşırı yük çözümlemesine katılmayacaktır .

n3421 , "Şeffaf Operatör İşlevleri" için bir örnek sağlar .

Tam kod burada .


1
std::set<std::string>"Geçmek" gerçekten fayda sağlıyor mu char const *, yoksa bir yapmanız mı gerekiyor std::set<std::string, std::less<>>?
Kerrek SB

@Kerrek Eğer yanılmıyorsam, "char const'ı geçmek *" nin kaçınmaya çalıştıkları problem olduğunu düşünüyorum. With the change proposed by N3465 the std::set::find() function would be an unconstrained template which would pass the const char* through to the comparator function, std::less<std::string>, which would construct a std::string temporary for every comparison. The LWG considered this performance problem to be a serious issue.

Sizin alıntınız ve 13. paragrafın benimki tam tersini söylüyor: "tür yoksa / yoksa" ...?!
Kerrek SB

4
@KerrekSB, bu benim hatam, N3657'nin "var" demesi gerekiyordu ama ben "yok" yazdım ... son dakikada yazılmış geç bir yazıydı. Taslak standart doğru.
Jonathan Wakely

3
Evet, o sırada söylediğimi değil, söylemek istediğimden alıntı yapmak daha açık olabilir :)
Jonathan Wakely

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.