关于 C++ SFINAE 的学习笔记1

SFINAE 是C++ 11 推出的一种模板匹配机制,SFINAE最主要的作用,是保证编译器在泛型函数、偏特化、及一般重载函数中遴选函数原型的候选列表时不被打断。

简单来说就是SFINAE机制可以在编译器进行函数匹配发生Error时,并不会立即报错,而是继续查找正确匹配的模板,可以利用该机制完成对不同类型的同种操作的分类的操作

1.使用decltype(A,B)的形式来作为模板函数的参数列表,A用于甄别传入类型,而B用于初始化实际的参数列表,因为()表达式的值为最右边的值。

template<typename T>
struct hasSerialize {
    template<typename C>
    static constexpr decltype(std::declval<C>().serialize(), bool()) test(int) {
        return true;
    }

    template<typename C>
    static constexpr bool test(...) {
        return false;
    }
      

    static constexpr bool value = test<T>(int());
};

代码中涉及到的decay_t
decay 使变量类型退化,数组退化为指针,函数退化为函数指针,其他退化为原始类型
    a. T为数组U或数组U引用,则type为U*.
    b. T为函数时,则type为std::add_pointer::type.
    c. 其它类型则移除cv限定符(const和volatile),则type为std::remove_cv<std::remove_reference::type>::type.
        std::declval 返回对象的右值引用,不管对象是否有构造函数,一般配合decltype使用
        因此 std::declval<C>().serialize() 表达式即为调用类型 C 的 serialize 成员函数,如果类型C没有 serialize 成员函数,编译器并不会马上报错,而是认为该模板不能够匹配当前传入的参数,并且会继续查找后续其他的模板函数来匹配传入的参数。利用这种方式可以做到对类型的判断.

2.利用 类模板 的传入类型来进行类型判断
 

template<typename T, typename U =std::string>
struct hasSerialize : std::false_type {

};
template<typename T>
struct hasSerialize<T, decltype(std::declval<T>().serialize())> : std::true_type {
    //继承了true_type 因此就拥有 了true_type的成员 value
};//偏特化,第二个参数为string类型(可以使用serialize进行转化)

两个同名的hasSerialize类模板,分别继承了std::true_type 和 std::false_type ,并且第一个模板的第二个type参数默认为string,而第二个模板的第二个type为  decltype(std::declval<T>().serialize()),因此会过滤掉不不能调用 .serialize() 成员函数 的类型 T

3、利用函数模板的偏特化来进行遴选

template<class T>
struct hasSerialize {
    // 编译时比较
    typedef char yes[1];
    typedef char no[2];

    template<typename U, U u>
    struct reallyHas;

    // std::string (C::*)() 是函数指针声明
    template<typename C>
    static yes &test(reallyHas<std::string (C::*)(), &C::serialize> * /*unused*/) {}

    template<typename C>
    static yes &test(reallyHas<std::string (C::*)() const, &C::serialize> * /*unused*/) {}

    template<typename>
    static no &test(...) { /* dark matter */ }

    //用作测试的返回值的常数。
    //由于编译时评估的大小,因此实际上在这里完成了测试。
    static constexpr bool value = sizeof(test<T>(0)) == sizeof(yes);
};

这种方式能够严格的检查是否含有 名为 serialize 且返回类型为 string 的函数
但是使用这种方式进行选择类型需要满足一下情况:
第二个参数必须是第一个参数的类型
它仅适用于 整数常量指针 (因此函数指针可以使用) ,例如:reallyHas<std::string (C::*)(), &C::serialize> 替换为 reallyHas<std::string (C::*)(), std::string (C::*)() &C::serialize> 并起作用。
因为

constexpr的作用是指 尽量将计算放在编译器,用法有if constexpr  ,constexpr修饰函数,修饰结构体构造函数

    1、constexpr修饰变量: 可以使得变量的计算在编译期进行,但是变量初始化的赋值必须是常量表达式
    
    2、在constexpr修饰函数时:
    修饰的函数只能包括return 语句。
    修饰的函数只能引用全局const常量。
    修饰的函数只能调用其他constexpr修饰的函数。
    该函数必须有返回值,即函数的返回值类型不能是 void。
    该函数只有在所有参数都是常量表达式,且返回的结果被用于常量表达式时,才会触发编译期求值


    3、constexpr修饰类的构造函数:
    constexpr Rectangle (int h, int w) : _h(h), _w(w) {} 使用constexpr修饰结构体的构造函数
    constexpr Rectangle obj(10, 20);  在构造结构体对象时使用constexpr进行修饰可以在编译器构建好结构体对象
    但是需要注意的是被constexpr修饰的结构体在初始化时 以及 被constexpr 修饰的函数 传参时都需要传入 全局const常量

   

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值