enable_if
Klasa
Warunkowo tworzy wystąpienie typu dla rozpoznawania przeciążenia SFINAE. Zagnieżdżona definicja enable_if<Condition,Type>::type
typu istnieje — i jest synonimem — Type
jeśli i tylko wtedy, gdy Condition
ma wartość true
.
Składnia
template <bool B, class T = void>
struct enable_if;
Parametry
B
Wartość określająca istnienie typu wynikowego.
T
Typ do utworzenia wystąpienia, jeśli B
ma wartość true
.
Uwagi
Jeśli B
parametr ma true
wartość , enable_if<B, T>
ma zagnieżdżoną definicję typu o nazwie "type", która jest synonimem .T
Jeśli B
parametr ma false
wartość , enable_if<B, T>
nie ma zagnieżdżonej definicji typu o nazwie "type".
Ten szablon aliasu jest udostępniany:
template <bool B, class T = void>
using enable_if_t = typename enable_if<B,T>::type;
W języku C++błąd podstawiania parametrów szablonu nie jest błędem — jest to nazywane SFINAE (niepowodzenie podstawienia nie jest błędem). enable_if
Zwykle służy do usuwania kandydatów z rozpoznawania przeciążenia — czyli uśmierc zestaw przeciążeń — dzięki czemu można odrzucić jedną definicję na rzecz innego. Jest to zgodne z zachowaniem SFINAE. Aby uzyskać więcej informacji na temat SFINAE, zobacz Błąd podstawiania nie jest błędem w Wikipedii.
Oto cztery przykładowe scenariusze:
- Scenariusz 1. Zawijanie zwracanego typu funkcji:
template <your_stuff>
typename enable_if<your_condition, your_return_type>::type
yourfunction(args) {// ...
}
// The alias template makes it more concise:
template <your_stuff>
enable_if_t<your_condition, your_return_type>
yourfunction(args) {// ...
}
- Scenariusz 2. Dodawanie parametru funkcji z argumentem domyślnym:
template <your_stuff>
your_return_type_if_present
yourfunction(args, enable_if_t<your condition, FOO> = BAR) {// ...
}
- Scenariusz 3. Dodawanie parametru szablonu z argumentem domyślnym:
template <your_stuff, typename Dummy = enable_if_t<your_condition>>
rest_of_function_declaration_goes_here
- Scenariusz 4: Jeśli funkcja ma argument inny niż szablon, możesz opakowować jej typ:
template <typename T>
void your_function(const T& t,
enable_if_t<is_something<T>::value, const string&>
s) {// ...
}
Scenariusz 1 nie działa z konstruktorami i operatorami konwersji, ponieważ nie mają typów zwracanych.
Scenariusz 2 pozostawia parametr bez nazwy. Można powiedzieć ::type Dummy = BAR
, ale nazwa Dummy
jest nieistotna i nadanie jej nazwy może wyzwolić ostrzeżenie "nieużywanego parametru". Musisz wybrać typ parametru FOO
funkcji i BAR
argument domyślny. Można powiedzieć int
i 0
, ale wtedy użytkownicy kodu mogą przypadkowo przekazać do funkcji dodatkową liczbę całkowitą, która byłaby ignorowana. Zamiast tego zalecamy użycie void **
polecenia i albo 0
lub nullptr
dlatego, że prawie nic nie jest konwertowane na void **
:
template <your_stuff>
your_return_type_if_present
yourfunction(args, typename enable_if<your_condition, void **>::type = nullptr) {// ...
}
Scenariusz 2 działa również w przypadku zwykłych konstruktorów. Nie działa jednak w przypadku operatorów konwersji, ponieważ nie mogą przyjmować dodatkowych parametrów. Nie działa również w przypadku variadic
konstruktorów, ponieważ dodanie dodatkowych parametrów sprawia, że parametr funkcji pakuje kontekst nieudukowany, a tym samym pokonuje cel enable_if
.
W scenariuszu 3 jest używana nazwa Dummy
, ale jest ona opcjonalna. Po prostu " typename = typename
będzie działać, ale jeśli uważasz, że wygląda to dziwnie, możesz użyć nazwy "fikcyjnej" — po prostu nie używaj nazwy, która może być również używana w definicji funkcji. Jeśli nie nadasz typu enable_if
, domyślnie ma wartość void i jest to całkowicie uzasadnione, ponieważ nie obchodzi cię to, co Dummy
jest. Działa to dla wszystkich elementów, w tym operatorów konwersji i variadic
konstruktorów.
Scenariusz 4 działa w konstruktorach, które nie mają typów zwracanych, a tym samym rozwiązuje ograniczenie opakowujące scenariusz 1. Jednak scenariusz 4 jest ograniczony do argumentów funkcji innych niż szablony, które nie są zawsze dostępne. (Użycie scenariusza 4 w argumencie funkcji szablonu uniemożliwia odliczeń argumentów szablonu z pracy nad nim).
enable_if
jest potężny, ale również niebezpieczny, jeśli jest niewłaściwy. Ponieważ jego celem jest, aby kandydaci zniknął przed rozwiązaniem przeciążenia, gdy jest niewłaściwie używany, jego skutki mogą być bardzo mylące. Poniżej przedstawiono kilka rekomendacji:
Nie należy wybierać
enable_if
między implementacjami w czasie kompilacji. Nigdy nie zapisuj goenable_if
dlaCONDITION
i drugiego dla!CONDITION
. Zamiast tego należy użyć wzorca wysyłania tagów — na przykład algorytmu, który wybiera implementacje w zależności od mocnych stron iteratorów, które są im podane.Nie używaj
enable_if
polecenia , aby wymusić wymagania. Jeśli chcesz zweryfikować parametry szablonu i jeśli walidacja zakończy się niepowodzeniem, przyczyną błędu zamiast wybrania innej implementacji użyj poleceniastatic_assert
.Użyj
enable_if
polecenia , gdy masz zestaw przeciążenia, który sprawia, że w przeciwnym razie dobry kod jest niejednoznaczny. Najczęściej występuje to w niejawnie konwertowanych konstruktorach.
Przykład
W tym przykładzie wyjaśniono, jak funkcja std::make_pair()
szablonu standardowej biblioteki języka C++ korzysta z enable_if
funkcji .
void func(const pair<int, int>&);
void func(const pair<string, string>&);
func(make_pair("foo", "bar"));
W tym przykładzie make_pair("foo", "bar")
zwraca wartość pair<const char *, const char *>
. Rozpoznawanie przeciążenia musi określać, które func()
chcesz użyć. pair<A, B>
program ma niejawnie konwertujący konstruktor z pair<X, Y>
klasy . Nie jest to nowe — był w języku C++98. Jednak w języku C++98/03 podpis konstruktora niejawnie istnieje, nawet jeśli jest pair<int, int>(const pair<const char *, const char *>&)
to . Rozpoznawanie przeciążeń nie obchodzi, że próba utworzenia wystąpienia tego konstruktora eksploduje strasznie, ponieważ const char *
nie jest niejawnie konwertowana na int
; patrzy tylko na podpisy, zanim zostaną utworzone wystąpienia definicji funkcji. W związku z tym przykładowy kod jest niejednoznaczny, ponieważ istnieją podpisy do konwersji pair<const char *, const char *>
na zarówno pair<int, int>
, jak i pair<string, string>
.
Język C++11 rozwiązał tę niejednoznaczność, używając polecenia enable_if
, aby upewnić się, że pair<A, B>(const pair<X, Y>&)
istnieje tylko wtedy, gdy const X&
jest niejawnie konwertowany na A
i const Y&
jest niejawnie konwertowany na B
. Umożliwia to rozpoznawanie przeciążenia w celu określenia, że pair<const char *, const char *>
nie jest konwertowane na pair<int, int>
i że przeciążenie, które przyjmuje pair<string, string>
, jest opłacalne.
Wymagania
Nagłówek: <type_traits>
Przestrzeń nazw: std