std::enable_if
Определено в заголовке <type_traits> | ||
|---|---|---|
template< bool B, class T = void > struct enable_if; | (с C++11) |
Если B является true, то std::enable_if имеет public member typedef type, равный T; в противном случае, member typedef отсутствует.
Эта метафункция является удобным способом использования SFINAE до C++20's concepts, в частности для условного удаления функций из набора кандидатов на основе свойств типов, позволяя отдельные перегрузки или специализации функций, основанные на этих свойствах типов.
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; }; |
Примечания
Распространённая ошибка — объявление двух шаблонов функций, которые различаются только своими аргументами по умолчанию. Это не работает, потому что объявления обрабатываются как повторные объявления одного и того же шаблона функции (аргументы по умолчанию не учитываются при определении эквивалентности шаблонов функций).
/* WRONG */
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) {} // error: treated as redefinition
};
/* RIGHT */
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, не включают части параметра шаблона нетипового типа, зависящие от экземпляции, в называние, что означает, что специализации двух различных шаблонов функций могут получить одно и то же называние и быть ошибочно связаны вместе. Например:
// first translation unit
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
// second translation unit
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, enabled via the return type
template<class T>
typename std::enable_if<std::is_trivially_default_constructible<T>::value>::type
construct(T*)
{
std::cout << "default constructing trivially default constructible T\n";
}
// same as above
template<class T>
typename std::enable_if<!std::is_trivially_default_constructible<T>::value>::type
construct(T* p)
{
std::cout << "default constructing non-trivially default constructible T\n";
::new(detail::voidify(p)) T;
}
// #2
template<class T, class... Args>
std::enable_if_t<std::is_constructible<T, Args&&...>::value> // Using helper type
construct(T* p, Args&&... args)
{
std::cout << "constructing T with operation\n";
::new(detail::voidify(p)) T(static_cast<Args&&>(args)...);
}
// #3, enabled via a parameter
template<class T>
void destroy(
T*,
typename std::enable_if<
std::is_trivially_destructible<T>::value
>::type* = 0)
{
std::cout << "destroying trivially destructible T\n";
}
// #4, enabled via a non-type template parameter
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 << "destroying non-trivially destructible T\n";
t->~T();
}
// #5, enabled via a type template parameter
template<class T,
typename = std::enable_if_t<std::is_array<T>::value>>
void destroy(T* t) // note: function signature is unmodified
{
for (std::size_t i = 0; i < std::extent<T>::value; ++i)
destroy((*t)[i]);
}
/*
template<class T,
typename = std::enable_if_t<std::is_void<T>::value>>
void destroy(T* t) {} // error: has the same signature with #5
*/
// the partial specialization of A is enabled via a template parameter
template<class T, class Enable = void>
class A {}; // primary template
template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
{}; // specialization for floating point types
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), "Hello");
destroy(reinterpret_cast<std::string*>(&u));
A<int>{}; // OK: matches the primary template
A<double>{}; // OK: matches the partial specialization
}Вывод:
default constructing trivially default constructible T destroying trivially destructible T constructing T with operation destroying non-trivially destructible T
См. также
|
(C++17) | Шаблон-псевдоним void для аргументов-параметров (шаблон-псевдоним) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/types/enable_if