Udostępnij za pośrednictwem


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 — Typejeś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 truewartość , enable_if<B, T> ma zagnieżdżoną definicję typu o nazwie "type", która jest synonimem .T

Jeśli B parametr ma falsewartość , 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 = typenamebę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 go enable_if dla CONDITION 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 polecenia static_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_iffunkcji .

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

Zobacz też

<type_traits>