std:: enable_if
|
Определено в заголовочном файле
<type_traits>
|
||
|
template
<
bool
B,
class
T
=
void
>
struct enable_if ; |
(начиная с C++11) | |
Если
B
имеет значение
true
,
std::enable_if
содержит открытый typedef-член
type
, равный
T
; в противном случае, typedef-член отсутствует.
Эта метафункция представляет собой удобный способ использования SFINAE до появления концепций в C++20, в частности для условного исключения функций из набора кандидатов на основе характеристик типов, что позволяет создавать отдельные перегрузки функций или специализации на основе различных характеристик типов.
std::enable_if
может использоваться во многих формах, включая:
- в качестве дополнительного аргумента функции (не применимо к большинству перегруженных операторов),
- в качестве типа возвращаемого значения (не применимо к конструкторам и деструкторам),
- в качестве параметра шаблона класса или шаблона функции.
Если программа добавляет специализации для
std::enable_if
, поведение не определено.
Содержание |
Типы членов
| Тип | Определение |
type
|
либо
T
, либо отсутствует такой член, в зависимости от значения
B
|
Вспомогательные типы
|
template
<
bool
B,
class
T
=
void
>
using enable_if_t = typename enable_if < B,T > :: type ; |
(начиная с C++14) | |
Возможная реализация
template<bool B, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; }; |
Примечания
Распространённой ошибкой является объявление двух шаблонов функций, которые отличаются только аргументами шаблона по умолчанию. Это не работает, поскольку объявления рассматриваются как повторные объявления одного и того же шаблона функции (аргументы шаблона по умолчанию не учитываются при эквивалентности шаблонов функций ).
/* НЕПРАВИЛЬНО */ struct T { enum { int_t, float_t } type; template<typename Integer, typename = std::enable_if_t<std::is_integral<Integer>::value>> T(Integer) : type(int_t) {} template<typename Floating, typename = std::enable_if_t<std::is_floating_point<Floating>::value>> T(Floating) : type(float_t) {} // ошибка: рассматривается как переопределение }; /* ПРАВИЛЬНО */ struct T { enum { int_t, float_t } type; template<typename Integer, std::enable_if_t<std::is_integral<Integer>::value, bool> = true> T(Integer) : type(int_t) {} template<typename Floating, std::enable_if_t<std::is_floating_point<Floating>::value, bool> = true> T(Floating) : type(float_t) {} // OK };
Следует проявлять осторожность при использовании
enable_if
в типе параметра шаблона-константы шаблона функции в области пространства имен. Некоторые спецификации ABI, такие как Itanium ABI, не включают зависящие от инстанциации части параметров шаблона-константы в манглинг имен. Это означает, что специализации двух различных шаблонов функций могут получить одинаковое манглированное имя и ошибочно быть слинкованы вместе. Например:
// первый модуль трансляции struct X { enum { value1 = true, value2 = true }; }; template<class T, std::enable_if_t<T::value1, int> = 0> void func() {} // #1 template void func<X>(); // #2 // второй модуль трансляции struct X { enum { value1 = true, value2 = true }; }; template<class T, std::enable_if_t<T::value2, int> = 0> void func() {} // #3 template void func<X>(); // #4
Шаблоны функций #1 и #3 имеют различные сигнатуры и являются разными шаблонами. Тем не менее, #2 и #4, несмотря на то что являются инстанциациями разных шаблонов функций, имеют одинаковое манглированное имя
в Itanium C++ ABI
(
_Z4funcI1XLi0EEvv
), что означает, что линковщик ошибочно будет считать их одной и той же сущностью.
Пример
#include <iostream> #include <new> #include <string> #include <type_traits> namespace detail { void* voidify(const volatile void* ptr) noexcept { return const_cast<void*>(ptr); } } // #1, включено через возвращаемый тип template<class T> typename std::enable_if<std::is_trivially_default_constructible<T>::значение>::type construct(T*) { std::cout << "конструирование по умолчанию тривиально конструируемого по умолчанию T\n"; } // то же самое template<class T> typename std::enable_if<!std::is_trivially_default_constructible<T>::значение>::type construct(T* p) { std::cout << "конструирование по умолчанию не тривиально конструируемого по умолчанию T\n"; ::new(detail::voidify(p)) T; } // #2 template<class T, class... Args> std::enable_if_t<std::is_constructible<T, Args&&...>::значение> // Использование вспомогательного типа construct(T* p, Args&&... args) { std::cout << "конструирование T с операцией\n"; ::new(detail::voidify(p)) T(static_cast<Args&&>(args)...); } // #3, включено через параметр template<class T> void destroy( T*, typename std::enable_if< std::is_trivially_destructible<T>::значение >::type* = 0) { std::cout << "уничтожение тривиально разрушаемого T\n"; } // #4, включено через константный параметр шаблона template<class T, typename std::enable_if< !std::is_trivially_destructible<T>{} && (std::is_class<T>{} || std::is_union<T>{}), bool>::type = true> void destroy(T* t) { std::cout << "уничтожение нетривиально разрушаемого T\n"; t->~T(); } // #5, включено через параметр шаблона типа template<class T, typename = std::enable_if_t<std::is_array<T>::значение>> void destroy(T* t) // примечание: сигнатура функции не изменена { for (std::size_t i = 0; i < std::extent<T>::значение; ++i) destroy((*t)[i]); } /* template<class T, typename = std::enable_if_t<std::is_void<T>::value>> void destroy(T* t) {} // ошибка: имеет ту же сигнатуру, что и #5 */ // частичная специализация A включена через параметр шаблона template<class T, class Enable = void> class A {}; // первичный шаблон template<class T> class A<T, typename std::enable_if<std::is_floating_point<T>::значение>::type> {}; // специализация для типов с плавающей точкой int main() { union { int i; char s[sizeof(std::string)]; } u; construct(reinterpret_cast<int*>(&u)); destroy(reinterpret_cast<int*>(&u)); construct(reinterpret_cast<std::string*>(&u), "Привет"); destroy(reinterpret_cast<std::string*>(&u)); A<int>{}; // OK: соответствует основному шаблону A<double>{}; // OK: соответствует частичной специализации }
Вывод:
конструирование по умолчанию тривиально конструируемого по умолчанию T уничтожение тривиально уничтожаемого T конструирование T с операцией уничтожение нетривиально уничтожаемого T
Смотрите также
|
(C++17)
|
void вариативный псевдоним шаблона
(псевдоним шаблона) |