Класс enable_if

Условно создает экземпляр типа для разрешения перегрузки SFINAE. Вложенное определение типа enable_if<Condition,Type>::type (синоним для Type) существует, если и только если значение Condition равно true.

Синтаксис

template <bool B, class T = void>
struct enable_if;

Параметры

B
Значение, определяющее наличие результирующего типа.

T
Тип для создания экземпляра, если B есть true.

Замечания

Если B имеет trueenable_if<B, T> вложенный типdef с именем typedef с именем type, который является синонимомT.

Если B это falseтак, enable_if<B, T> не имеет вложенного типа с именем "type".

Предоставляется следующий шаблон псевдонима.

template <bool B, class T = void>
using enable_if_t = typename enable_if<B,T>::type;

В C++сбой подстановки параметров шаблона не является ошибкой в самом деле— это называется SFINAE (сбой подстановки не является ошибкой). Обычно enable_if используется для удаления кандидатов из разрешения перегрузки, т. е. функция отбраковывает набор перегрузки, чтобы одно определение было отброшено в пользу другого. Это соответствует поведению SFINAE. Дополнительные сведения о SFINAE см. в разделе "Сбой подстановки" не является ошибкой в Википедии.

Вот 4 примера сценариев.

  • Сценарий 1. Упаковка возвращаемого типа функции:
    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) {// ...
}
  • Сценарий 2. Добавление параметра функции с аргументом по умолчанию:
    template <your_stuff>
your_return_type_if_present
    yourfunction(args, enable_if_t<your condition, FOO> = BAR) {// ...
}
  • Сценарий 3. Добавление параметра шаблона с аргументом по умолчанию:
    template <your_stuff, typename Dummy = enable_if_t<your_condition>>
rest_of_function_declaration_goes_here
  • Сценарий 4. Если функция содержит аргумент без шаблона, ее тип можно упаковать:
    template <typename T>
void your_function(const T& t,
    enable_if_t<is_something<T>::value, const string&>
s) {// ...
}

Сценарий 1 не применяется к конструкторам и операторам преобразования, так как у них нет возвращаемых типов.

В сценарии 2 параметр остается без имени. Можно использовать ::type Dummy = BAR, но имя Dummy не играет роли, поэтому указание имени, вероятно, вызовет предупреждение о параметре без ссылки. Необходимо выбрать тип параметра функции FOO и аргумент по умолчанию BAR. Можно использовать int и 0, но тогда пользователи кода смогут случайно передать функции дополнительное целое число, которое будет проигнорировано. Мы рекомендуем использовать void ** и значение 0 или nullptr, так как почти ничего нельзя преобразовать в тип void **.

template <your_stuff>
your_return_type_if_present
yourfunction(args, typename enable_if<your_condition, void **>::type = nullptr) {// ...
}

Сценарий 2 также подходит для обычных конструкторов. Но он не работает с операторами преобразования, так как они не могут принимать дополнительные параметры. Он также не работает для variadic конструкторов, так как добавление дополнительных параметров делает пакет параметров функции неисключаемой контекстом и тем самым побеждает назначение enable_if.

В сценарии 3 используется имя Dummy, но это необязательно. Подойдет и просто "typename = typename", но, если вы считаете, что это выглядит странно, можно использовать имя-заглушку (только не применяйте имя, которое может использоваться в определении функции). Если не передать тип функции enable_if, по умолчанию используется тип void, и это вполне логично, так как Dummy не играет никакой роли. Это работает для всего, включая операторы преобразования и variadic конструкторы.

Сценарий 4 работает для конструкторов без возвращаемых типов, что устраняет ограничение упаковки сценария 1. Однако сценарий 4 применяется только для аргументов функции без шаблонов, которые не всегда доступны. (При использовании сценария 4 для аргументов функции на основе шаблона устранение аргументов шаблона не работает.)

enable_if — это мощное средство, которое может быть опасным при неправильном использовании. Так как цель функции — удалить кандидаты до разрешения перегрузки, при ее неправильном применении результаты могут быть очень запутанными. Вот несколько рекомендаций.

  • Не используйте enable_if выбор между реализациями во время компиляции. Не пишите одну функцию enable_if для CONDITION и другую для !CONDITION. Используйте шаблон отправки тегов, например, алгоритм может выбирать реализации в зависимости от силы указанных итераторов.

  • Не используйте enable_if для принудительного применения требований. Если вы хотите проверить параметры шаблона, а если проверка завершается ошибкой, вместо выбора другой реализации используйте static_assert.

  • Используйте enable_if при наличии набора перегрузок, который делает код неоднозначным. Чаще всего это происходит в конструкторах с неявным преобразованием.

Пример

В этом примере объясняется, как функция std::make_pair() шаблона стандартной библиотеки C++ использует преимущества enable_if.

void func(const pair<int, int>&);

void func(const pair<string, string>&);

func(make_pair("foo", "bar"));

В этом примере make_pair("foo", "bar") возвращает pair<const char *, const char *>. Разрешение перегрузок должно определить требуемую функцию func(). pair<A, B> содержит конструктор с неявным преобразованием из pair<X, Y>. Это не новый элемент, он был представлен в C++98. Однако в C++98/03 сигнатура конструктора с неявным преобразованием всегда существует, даже если это pair<int, int>(const pair<const char *, const char *>&). Процессу разрешения перегрузок все равно, что попытка создать экземпляр конструктора приведет к отрицательным последствиям, так как const char * нельзя неявно преобразовать в int. Процесс смотрит только на сигнатуры перед созданием экземпляра определений функций. Поэтому этот пример кода неоднозначен, так как существуют сигнатуры для преобразования pair<const char *, const char *> в pair<int, int> и pair<string, string>.

В C++11 эта неоднозначность была устранена с помощью enable_if, чтобы конструкция pair<A, B>(const pair<X, Y>&) существовала только, если const X& неявно преобразуется в A, а const Y& неявно преобразуется в B. Это позволяет разрешать перегрузку, чтобы определить, pair<const char *, const char *> что не преобразуется в pair<int, int> и что перегрузка, которая принимается pair<string, string> , является жизнеспособной.

Требования

Заголовок.<type_traits>

Пространство именstd:

См. также

<type_traits>