C++元编程之enable_if

std::enable_if的实现原理

主要使用了SFINAE(Substitution Is Not A Error)原理。

推断失败并不是错误,应当通过控制编译器来选择正确的方法。

关于SFINE

这个就是SFINAE的思想。通过C++来体现SFINAE的编程技巧如例1所示。

#include<iostream>
#include<type_traits>

template<bool, typename T=void>
struct test_SFINAE {
};

template<typename T>   //partial specialization
struct test_SFINAE<true,T> { //#5
    typedef int type;
};

template<typename T, typename test_SFINAE<std::is_integral<T>::value, bool>::type = 0>
void test_SFINAE_func()   // #3.Only T is int can implement successfully.
{
    std::cout << "It is int" << std::endl;
}

template<typename T, typename test_SFINAE<std::is_floating_point<T>::value,bool>::type = 0>
void test_SFINAE_func()    //#4.Only T is float can implement successfully.
{
    std::cout << "It is float" << std::endl;
}

void _test_all()
{
    test_SFINAE_func<int>(); //#1 "It is int"
    test_SFINAE_func<float>(); //#2 "It is float"
}
例1

例1中可以看到,#1被具体化成int,因为#4中,只有float才可以编译成功,所以编译器选择了#3的模板函数。编译器的判断顺序如下图1所示。

T为int类型
std::is_floating_point::value为false
test_template::value,bool>::type = 0>
被推断成test_template::type = 0>
因为test_SFINAE被定义为{},没声明type,所以test_SFINAE::type无法编译成功
同时template::value, bool>::type = 0>
被具体化成template::type = 0>
因为#5已经偏特化了test_SFINAE的模板,里面定义了type,
因此template::type = 0>编译成功,
编译器选择了#3进行具体化

以上就是演示控制编译器进行SFINAE思想的过程。总的来讲就是

告诉编译器按照这个类型进行具体化是错误的 --> 同时提供一个可以将这个类型进行正确具体化的模板

enable_if的实现

大部分编译器的实现方法如下

template<bool, typename T=void>
struct enable_if{};

template<typename T>
struct enable_if<true, T>{}
    T type;
;

可以看到我的例1就是仿照enable_if的实现>_<。

tips:

关于is_integral, is_floating_point的使用方法和原理可以网上搜索了解。

使用enable_if的场景

上面了解到了enable_if实现的基本原理,那么现在介绍一下用到这个功能的

  • 作为函数的返回值(控制函数的返回类型)
  • 作为函数的入参(控制入参类型的值,不怎么使用到)
  • 作为模板的模板参数(控制编译器使用正确的模板)

enable_if经常与is_xxx(例如上面例子的is_integral)进行搭配,用来进行各种的类型判断和检查是否存在成员的场景用到。

使用std::enable_if的陷阱

  • 注意使用is_xxx的时候,如果是同时使用is_xxx的特性,模板中需要使用非类型参数来替代类型参数,这里使用cppreference的例子来展示。
/* WRONG */
 
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) {} // error: treated as redefinition
};
 
/* RIGHT */
 
struct T {
    enum { int_t, float_t } type;
    template <typename Integer,
              std::enable_if_t<std::is_integral<Integer>::value, bool> = true  //#1
    >
    T(Integer) : type(int_t) {}
 
    template <typename Floating,
              std::enable_if_t<std::is_floating_point<Floating>::value, bool> = true  //#2
    >
    T(Floating) : type(float_t) {} // OK
};
from cppreference

注意#1中=ture,因为这个在#1中代表了模板非类型参数的默认值,所以在#1中可以false来修改成false的默认值也是可以的,因为代码中并没有使用到这个值。同时也可以修改bool的类型,改成char或者int等也可以,只是不同类型占用的字节数可能会更大。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值