目录
std::enable_if的使用
std::enable_if
- C++11新标准中引入的类模板(结构模板),使用体现了C++编译器的SFINAE特性。
- 定位为一个helper模板(助手模板),用于辅助其他模板的设计,表现一种:编译期的分支逻辑(编译期就可以确定走哪条分支)。
template <typename T> struct MEB { using type = T; };
- 调用
MEB<int>::type abc = 15; //MEB<int>::type 就代表int类型
std::enable_if源码
// STRUCT TEMPLATE enable_if template <bool _Test, class _Ty = void> //泛化版本 struct enable_if {}; // no member "type" when !_Test template <class _Ty> //偏特化版本:怎么理解:只有这个偏特化版本存在,才存在一个名字叫做type的类型别名(类型) struct enable_if<true, _Ty> { // type is _Ty for _Test using type = _Ty; };
偏特化完全可以理解成一种(在编译期)条件分支语句。
- 编译期就知道哪个条件成立。
std::enable_if基础认识
std::enable_if< (3 > 2)>::type* mypoint1 = nullptr; //相当于 void *mypoint1 = nullptr
- 上面std::enable_if第一个参数为true,则执行偏特化版本分支,有type类型,且泛化版本已经给出了第二个参数的默认值为void,即type为void。
std::enable_if< (3 < 2)>::type* mypoint1 = nullptr;
- 上面std::enable_if第一个参数为false,执行泛化版本分支,泛化版本中没有type类型,所以报错。
enable_if应用于函数模板
典型应用是作为函数模板的返回类型
template <typename T> typename std::enable_if<(sizeof(T) > 2)>::type funceb() { //... }
- 调用
funceb<int>();
- 由于sizeof(int) 大于 2,即走偏特化版本分支,此时第二个参数为默认值void,所以模板实例化为void funceb(){}。
- 若调用如下
funceb<char>();
- sizeof(char) 小于2 不满足条件,即不符合这个函数模板,此模板被忽略掉,去寻找其他的funceb函数模板或函数,但是未能找到,报如下错误
enable_if_t (C++14)
template <bool _Test, class _Ty = void> using enable_if_t = typename enable_if<_Test, _Ty>::type; //别名模板
- 可进行如下替换
template <typename T> typename std::enable_if<(sizeof(T) > 2)>::type funceb() { //... } /******替换为*******/ template <typename T> std::enable_if_t<(sizeof(T) > 2)> funceb() { //... }
应用案例
template <typename T> std::enable_if_t<(sizeof(T) > 2),T> funceb() { T myt = {}; return myt; }
- 调用
funceb<int>();
- 模板实例化为int funceb(){}
enable_if用于类模板中
class Human { public: //构造函数模板 template<typename T> Human(T&& tmpname) : m_sname(std::forward<T>(tmpname)) { cout << "Human(T&& tmpname)执行" << endl; } //拷贝构造函数 Human(const Human& th) : m_sname(th.m_sname) { cout << "Human(const Human& th)拷贝构造函数执行" << endl; } //移动构造函数 Human(Human&& th) : m_sname(std::move(th.m_sname)) { cout << "Human(Human&& th)移动构造函数执行" << endl; } private: string m_sname; };
- 调用
string sname = "ZhangSan"; Human myhuman1(sname); Human myhuman3(myhuman1);
- 问题:实际编译器去调用了构造函数模板,而不是调用了拷贝构造函数。
问题解决思路
- 针对构造函数模板,如果给进来的参数是一个string类型的参数,就让这个构造函数模板生效。
- 否则就让这个构造函数模板被忽略即可。也就是说,如果使用enable_if于构造函数模板中,enable_if的条件只需要设置成“形参类型==string类型”即可。
[ 注 ]std::is_convertible(C++11)
- 一两个模板参数分别是From 和To:用于判断能否从某个类型隐式的转换到另外一个类型。
- 返回一个bool值——true或者false。
cout << "string=>float:" << std::is_convertible<string, float>::value << endl; //0 cout << "float=>int:" << std::is_convertible<float, int>::value << endl; //1
应用范例
class Human { //给类型模板定义一个别名(别名模板) template <typename T> using StrProcType = std::enable_if_t<std::is_convertible<T, std::string>::value >; public: //构造函数模板 template< typename T, typename = StrProcType<T> //如果T能够成功转换成std::string类型,那么typename = void > Human(T&& tmpname) : m_sname(std::forward<T>(tmpname)) { cout << "Human(T&& tmpname)执行" << endl; } //拷贝构造函数 Human(const Human& th) : m_sname(th.m_sname) { cout << "Human(const Human& th)拷贝构造函数执行" << endl; } //移动构造函数 Human(Human&& th) : m_sname(std::move(th.m_sname)) { cout << "Human(Human&& th)移动构造函数执行" << endl; } private: string m_sname; };
- 调用
string sname = "ZhangSan"; Human myhuman1(sname); Human myhuman3(myhuman1);
- 构造函数模板通过判断传入的参数类型是否为string,来判断执行还是忽略该模板,
- Human myhuman3(myhuman1); 传入的不是string类型,所以自动忽略构造函数模板,转而调用拷贝构造函数。
- 输出