std::enable_if
来自 cppreference.com
定义在头文件 <type_traits> 中 |
||
template< bool B, class T = void > struct enable_if; |
(自 C++11 起) | |
如果 B
为 true,std::enable_if
将具有一个公共成员类型定义 type
,它等于 T
;否则,将没有成员类型定义。
此元函数是在 C++20 的 概念 之前利用 SFINAE 的便捷方式,尤其是根据类型特征有条件地从 候选集 中删除函数,允许根据这些不同的类型特征分别进行函数重载或特化。
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,不包括非类型模板参数的依赖于实例化的部分,这意味着两个不同函数模板的特化最终可能具有相同的 mangled 名称,并被错误地链接在一起。例如
// 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
) 中具有相同的 mangled 名称,这意味着链接器会错误地认为它们是同一个实体。
[编辑] 示例
运行此代码
#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) |
可变参数别名模板 (别名模板) |