std::enable_if的使用
-
对于重载的函数或者函数模板的选择上,编译器内部有一个自己的规则,并不是简单粗暴的对函数就优先选择,对函数模板就靠后选择
-
替换失败并不是一个错误(SFINAE):
Substitution Failure Is Not An Error
,SFINAE看成是C++语言的一种特性或者说一种模板设计中要遵循的重要原则 -
针对函数模板而言,当用一个具体类型替换函数模板的参数时,可能会产生意想不到的问题:,比如产生一些毫无意义甚至是看起来语法上有错误的代码,对于这些代码,编译器并不一定报错,有可能是忽略。编译器认为这个函数模板不匹配针对本次的函数调用,就当这个函数不存在一样。转而去选择其他更匹配的函数或者函数模板
基础认识:C++11新标准中引入的类模板(结构模板。使用体现了C++编译器的SFINAE特性)
- 定位为一个helper模板(助手模板),用于辅助其他模板的设计,表现一种:编译器的分支逻辑(编译器就可以确定走哪条分支)
namespace nmsp1 {
template<typename T>
struct MEB
{
using type = T;
};
}
>查看下enable_id的源码
nmsp1::MEB<int>::type abc = 1; //就代表int类型
怎么理解这个篇特化版本:只有这个偏特化版本存在,才存在一个名字叫做type的类型别名(类型)
偏特化完全可以理解为一种编译器的条件分支语句
std::enable_if<true>::type* mypoint1 = nullptr;
//第二个有默认值,所以第二个采用void,那么type就是void
std::enable_if<false>::type* mypoint1 = nullptr;
//走的是泛化版本,false没有type这个别名
范例:enable_if用于函数模板中,典型应用是作为函数模板的返回类型
template<typename T>
typename std::enable_if<(sizeof(T) > 2)>::type funceb()
{
//....
}
nmsp2::funceb<int>();//void funceb(){}
nmsp2::funceb<char>();//error:未找到匹配的重载函数,条件不满足
//C++14出了这个等同上面
template<typename T>
std::enable_if_t<(sizeof(T) > 2),T> funceb()
{
T myt = {};
return myt;
}
nmsp2::funceb<int>();//int funceb(){}
//nmsp2::funceb<char>();
如果是第一条语句:如果funceb函数模板中涉及到enable_if_t中的条件成立的时候,这个funceb代表是一个类型,如果是第二条,条件不成立,那么有SFINAE的特性存在,请编译器忽略我这个funceb的这个函数模板吧。那么对第二条语句上例的funceb这个函数模板是不存在那样
enable_if_t源码,别名模板
示例:用于类模板中
万能引用
namespace nmsp3 {
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 = "zhangshan";
nmsp3::Human myhuman(sname);
// nmsp3::Human myhuman3(myhuman);//实际编译器调用构造函数模板,而不是拷贝构造函数
代码解决,针对构造函数,如果给进来的参数是一个string类型的参数,就让这个构造函数模板生效,否则就让这个构造函数模板被忽略即可,也就是说,如果使用enable_if于构造函数模板中,enable_if的条件只需要设置成"形参类型==string
std::is_convertible,C++11引入的,两个模板参数分别是From 和 To:用于判断能否从某个类型隐式的准换到另外一个类型,返回是布尔值
cout << "string=>float: " << std::is_convertible<string, float>::value << endl; //string=>float: 0
cout << "float>=int: " << std::is_convertible<float, int>::value << endl; //float>=int: 1
解决方案
namespace nmsp4 {
//别名模板
template<typename T>
using StrProcType = std::enable_if_t<std::is_convertible<T, std::string>::value>;
class Human {
public:
//构造函数模板
template<
typename T,
typename = std::enable_if_t<std::is_convertible<T,std::string>::value>
//如果T能够成功转换成std::string类型,那么typename = void
//typename = StrProcType<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 = "zhangshan";
nmsp4::Human myhuman(sname);
nmsp4::Human myhuman3(myhuman);//成功调用拷贝函数
private:
string m_sname;
};
}
string sname = "zhangshan";
nmsp4::Human myhuman(sname);
nmsp4::Human myhuman3(myhuman);//成功调用拷贝函数