std::enable_if

来自cppreference.com
< cpp‎ | types
 
 
元编程库
类型特征
类型类别
(C++11)
(C++11)(DR*)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11) 
类型属性
(C++11)
(C++11)
(C++14)
(C++11)(C++26 弃用)
(C++11)(C++20 前*)
(C++11)(C++20 弃用)
(C++11)
类型特征常量
元函数
(C++17)
受支持操作
关系与属性查询
类型修改
类型变换
(C++11)(C++23 弃用)
(C++11)(C++23 弃用)
(C++11)
(C++11)(C++20 前*)(C++17)

enable_if
(C++11)
(C++17)
编译时有理数算术
编译时整数序列
 
在标头 <type_traits> 定义
template<bool B, class T =void>
struct enable_if;
(C++11 起)

Btrue,则 std::enable_if 拥有等同于 T 的公开成员 typedef type;否则,无成员 typedef。

此元函数是在 C++20 的概念可用前,活用 SFINAE 的便利方法,尤其是基于类型特征从候选集中条件性地移除函数,并对不同类型特征提供分离的函数重载与特化。

std::enable_if 有多种用法,包括:

  • 用作额外的函数实参(不适用于大多数运算符重载)
  • 用作返回类型(不适用于构造函数与析构函数)
  • 用作类模板或函数模板形参。

如果程序添加了 std::enable_if 的特化,那么行为未定义。

目录

[编辑]成员类型

类型 定义
typeT 或无此成员,取决于 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   templatevoid func<X>();// #2   // 第二个翻译单元   struct X {enum{ value1 =true, value2 =true};};   template<class T, std::enable_if_t<T::value2, int>=0>void func(){}// #3   templatevoid func<X>();//#4

函数模板 #1 与 #3 拥有不同签名,且为不同的模板。不过 #2 与 #4 尽管是不同函数模板的实例化,却在 Itanium C++ ABI 中拥有相同的重整名(_Z4funcI1XLi0EEvv),这表示链接器会错误地认为它们是同一实体。

[编辑]示例

#include <iostream>#include <new>#include <string>#include <type_traits>   namespace detail {void* voidify(constvolatilevoid* ptr)noexcept{returnconst_cast<void*>(ptr);}}   // #1,经由返回类型启用template<class T>typename std::enable_if<std::is_trivially_default_constructible<T>::value>::type construct(T*){std::cout<<"默认构造可平凡默认构造的 T\n";}   // 同上template<class T>typename std::enable_if<!std::is_trivially_default_constructible<T>::value>::type construct(T* p){std::cout<<"默认构造不可平凡默认构造的 T\n";::new(detail::voidify(p)) T;}   // #2template<class T, class... Args> std::enable_if_t<std::is_constructible<T, Args&&...>::value>// 使用辅助类型 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>::value>::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>::value>>void destroy(T* t)// 注:不修改函数签名{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){} // 错误:函数签名与 #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>::value>::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),"Hello"); destroy(reinterpret_cast<std::string*>(&u));   A<int>{};// OK:匹配主模板 A<double>{};// OK:匹配偏特化}

输出:

默认构造可平凡默认构造的 T 销毁可平凡析构的 T 带操作构造 T 销毁不可平凡析构的 T

[编辑]参阅

(C++17)
void 变参别名模板
(别名模板)[编辑]
close