Поделиться через


Класс 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 равно true, enable_if<B, T> содержит вложенное определение типа "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 также подходит для обычных конструкторов. Но он не работает с операторами преобразования, так как они не могут принимать дополнительные параметры. Он также не подходит для конструкторов с переменным числом аргументов, так как из-за добавления параметров пакет параметров функции становится невыводимым контекстом, что противоречит цели enable_if.

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

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

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

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

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

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

Пример

В этом примере описывается, как функция-шаблон STL std::make_pair() использует 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>

Другие ресурсы

Члены <type_traits>