W C ++ 14 wydaje się, że kontenery asocjacyjne zmieniły się z C ++ 11 - [Associative.reqmts] / 13 mówi:
Szablony funkcji składowej
find
,count
,lower_bound
,upper_bound
, iequal_range
nie powinien uczestniczyć w rozdzielczości przeciążenia chyba typCompare::is_transparent
istnieje.
Jaki jest cel uczynienia komparatora „przejrzystym”?
C ++ 14 udostępnia również szablony bibliotek, takie jak ten:
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;
};
Tak na przykład, std::set<T, std::less<T>>
by nie mieć przejrzysty porównawczy, ale std::set<T, std::less<>>
będzie mieć.
Jaki problem to rozwiązuje i czy zmienia to działanie standardowych kontenerów? Na przykład, parametry szablonów std::set
są nadal Key, Compare = std::less<Key>, ...
, więc nie domyślny zestaw stracić swoje find
, count
itd członków?
Odpowiedzi:
Zobacz odpowiedź Dietmar za i odpowiedź remyabel użytkownika .
Nie, nie domyślnie.
Nowe przeciążenia szablonów funkcji składowych
find
itp. Pozwalają na użycie typu, który jest porównywalny z kluczem kontenera, zamiast używania samego typu klucza. Aby zapoznać się z uzasadnieniem i szczegółową, starannie napisaną propozycją dodania tej funkcji, zobacz N3465 autorstwa Joaquína Mª Lópeza Muñoza.Na spotkaniu w Bristolu LWG zgodziło się, że funkcja wyszukiwania heterogenicznego jest użyteczna i pożądana, ale nie mogliśmy być pewni, że propozycja Joaquína będzie bezpieczna we wszystkich przypadkach. Propozycja N3465 spowodowałaby poważne problemy w przypadku niektórych programów (patrz sekcja Wpływ na istniejący kod ). Joaquín przygotował zaktualizowaną wersję roboczą propozycji z kilkoma alternatywnymi wdrożeniami z różnymi kompromisami, co było bardzo przydatne, pomagając LWG zrozumieć zalety i wady, ale wszyscy ryzykowali zerwanie niektórych programów w jakiś sposób, więc nie było zgody co do dodania tej funkcji. Zdecydowaliśmy, że chociaż bezwarunkowe dodawanie tej funkcji nie byłoby bezpieczne, byłoby bezpieczne, gdyby była domyślnie wyłączona i tylko „włączono”.
Kluczową różnicą w propozycji N3657 (która była ostatnią poprawką przeze mnie i STL opartą na N3465 i późniejszą niepublikowaną wersją roboczą Joaquína) było dodanie
is_transparent
typu jako protokołu, którego można użyć do włączenia nowej funkcjonalności.Jeśli nie używasz „przezroczystego funktora” (tj. Takiego, który definiuje
is_transparent
typ), wtedy kontenery zachowują się tak samo, jak zawsze i nadal jest to ustawienie domyślne.Jeśli zdecydujesz się na użycie
std::less<>
(co jest nowością w C ++ 14) lub innego typu „przezroczystego funktora”, otrzymasz nową funkcjonalność.Korzystanie
std::less<>
z szablonów aliasów jest łatwe:Nazwa
is_transparent
pochodzi od języka STL N3421, który dodał „operatory diamentowe” do C ++ 14. „Przezroczysty funktor” to taki, który akceptuje dowolne typy argumentów (które nie muszą być takie same) i po prostu przekazuje te argumenty do innego operatora. Taki funktor jest dokładnie tym, czego potrzebujesz do wyszukiwania heterogenicznego w kontenerach asocjacyjnych, więc typis_transparent
został dodany do wszystkich operatorów rombu i użyty jako typ tagu, aby wskazać, że nowa funkcja powinna być włączona w kontenerach asocjacyjnych. Z technicznego punktu widzenia kontenery nie potrzebują „przezroczystego funktora”, tylko taki, który obsługuje wywoływanie go z typami heterogenicznymi (np.pointer_comp
Typ w https://stackoverflow.com/a/18940595/981959 nie jest przezroczysty zgodnie z definicją STL,pointer_comp::is_transparent
pozwala na użycie go do rozwiązania problemu). Jeśli tylko kiedykolwiek odnośnika w twojejstd::set<T, C>
z klawiszami typuT
iint
następnieC
musi być wpłacone z argumentami typu tylkoT
iint
(w dowolnej kolejności), to nie musi być naprawdę przejrzyste. Użyliśmy tej nazwy częściowo dlatego, że nie mogliśmy wymyślić lepszej nazwy (wolałbym,is_polymorphic
ponieważ takie funktory używają statycznego polimorfizmu, ale istnieje jużstd::is_polymorphic
cecha typu, która odnosi się do dynamicznego polimorfizmu).źródło
<>
powiązanej propozycji, ale ta propozycja nie wprowadziła<>
- jest to istniejąca składnia dla pustej listy parametrów szablonu. „Diamentowe funktory operatorów” byłyby nieco mniej zagmatwane.W C ++ 11 nie są członkami szablony
find()
,lower_bound()
itp Oznacza to, że nic nie jest stracone przez tę zmianę. Szablony członkowskie zostały wprowadzone wraz z n3657, aby umożliwić używanie heterogenicznych kluczy z kontenerami asocjacyjnymi. Nie widzę żadnego konkretnego przykładu, w którym byłoby to przydatne, z wyjątkiem przykładu, który jest dobry i zły!is_transparent
Stosowanie ma na celu uniknięcie niepotrzebnych konwersji. Jeśli szablony elementów członkowskich byłyby nieograniczone, istniejący kod może bezpośrednio przechodzić przez obiekty, które zostałyby przekonwertowane bez szablonów elementów członkowskich. Przykładowy przypadek użycia z n3657 polega na zlokalizowaniu obiektu w astd::set<std::string>
przy użyciu literału ciągu: w definicji C ++ 11std::string
obiekt jest konstruowany podczas przekazywania literałów ciągu do odpowiedniej funkcji składowej . Dzięki zmianie możliwe jest bezpośrednie użycie literału ciągu. Jeśli bazowy obiekt funkcji porównania jest zaimplementowany wyłącznie w kategoriachstd::string
, jest to złe, ponieważ terazstd::string
zostanie utworzony dla każdego porównania. Z drugiej strony, jeśli podstawowy obiekt funkcji porównania może przyjmować rozszerzeniestd::string
i literał łańcuchowy, który może uniknąć konstrukcji tymczasowego obiektu.Zagnieżdżony
is_transparent
typ w obiekcie funkcji porównania zapewnia sposób określenia, czy należy użyć funkcji składowej opartej na szablonie: jeśli obiekt funkcji porównania może obsługiwać heterogeniczne argumenty, definiuje ten typ, aby wskazać, że może efektywnie radzić sobie z różnymi argumentami. Na przykład nowe obiekty funkcji operatora po prostu delegująoperator<()
i twierdzą, że są przezroczyste. To przynajmniej działa, dlastd::string
którego przeciążono mniej niż operatorzy przyjmującychar const*
jako argument. Ponieważ te obiekty funkcyjne są również nowe, nawet jeśli robią niewłaściwe rzeczy (tj. Wymagają konwersji dla jakiegoś typu), przynajmniej nie byłaby to cicha zmiana powodująca pogorszenie wydajności.źródło
is_transparent
jest zdefiniowane w obiekcie funkcji porównania zgodnie z 23.2.4 [Associative.reqmts] paragraf 13. Domyślne obiekty funkcji porównania sąstd::less<Key>
zgodne z 23.4.2 [associative.map.syn] i 23.4. 3 [associative.set.syn]. Według 20.10.5 [Porównanie] pkt 4 ogólny szablon nastd::less<...>
nie nie określają typ zagnieżdżonyis_transparent
alestd::less<void>
specjalizacja robi. Oznacza to, że nie, domyślnie nie otrzymujesz przezroczystego operatora.is_transparent
?Poniżej znajduje się cała kopia makaronu z n3657 .
Cytując Yakka ,
i n3657,
n3421 zawiera przykład „przezroczystych funktorów operatora” .
Pełny kod jest tutaj .
źródło
std::set<std::string>
faktycznie korzyści płyną z „przejściachar const *
przez”, czy też trzeba to zrobićstd::set<std::string, std::less<>>
?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.
Stephan T Lavavej opowiada o problemach, w których kompilator tworzy elementy tymczasowe, oraz o tym, jak jego propozycja przezroczystych funktorów operatorowych rozwiąże to w języku c ++ 1y
GoingNative 2013 - nie pomagaj kompilatorowi (około godziny)
źródło