C++模板编程-enable_if

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);//成功调用拷贝函数

https://blog.csdn.net/baidu_41388533/article/details/109689556

https://blog.csdn.net/baidu_41388533/article/details/109702574

posted @ 2024-05-16 15:37  白伟碧一些小心得  阅读(11)  评论(0编辑  收藏  举报