Skip to content

Latest commit

 

History

History
executable file
·
81 lines (58 loc) · 3.33 KB

enable_if.md

File metadata and controls

executable file
·
81 lines (58 loc) · 3.33 KB

从模板偏特化看enable_if的实现

enable_if<bool, T> 是<type_traits> 下的一个模板元编程工具,其可以通过第一个模板参数bool值是否为true来决定该模板函数/类是否被编译,第二个参数则是附带的类型,可以通过enable_if<bool,T>::type 获取,不使用的话可以为void。

enable_if 本质上是一个结构体,并且定义非常简单:

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

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

​ 要介绍enable_if的实现原理,需要先介绍模板偏特化。

模板偏特化

​ 模板偏特化是一种类似于多态的技术,我们可以通过定义不同的函数参数来区分同一个名字的函数,类似的,我们可以通过不同的模板参数区分多个模板(不同模板可以有不同的实现)。但不同的是,多态函数的签名可以毫不相关,但是对于模板而言,除了第一个声明的模板,后面的模板都必须是第一个模板的特例,称为特化。

  1. 首先确定一个最泛化的模板(为了满足需要,模板参数只能多不能少)

    以三个模板参数为例:

    template <typename, typename, typename>
    struct A {
        void func() {
            printf("declaration1\n");
        }
    };
  2. 接着定义A的特化,以两个模板参数为例:

    template <typename T, typename U>//这里的模板参数不再重要,只是指示T和U是两个泛型
    struct A<bool, T, U> {//这里的特化方式更加重要,这里将第一个类型特化为bool值
        void func() {
            printf("declaration2\n");
        }
    };

    这时候定义的模板参数就已经不再重要,只需要看对于A的特化方式。不管是多少个模板参数,只要特化方式一致, 编译器就会看作同一个声明。

    template <typename T, typename U, typename K, typename V>//与前面的声明本质相同,如果同时出现会报错。
    struct A<bool, T, U> {
        void func() {
            printf("declaration2\n");
        }
    };
  3. 出现存在偏特化的类型对象的声明或者函数的调用时,编译器会优先匹配所有的特化模板,如果都不能匹配才会匹配第一个泛化模板,还不能匹配就会报错。

    所以对于上面的类型A,声明对象并进行调用:

    A<bool, int, double> a;//第一个参数为bool,优先匹配特化模板
    a.func();//调用特化模板的函数实现,输出declaration2

​ 需要强调的是,特化并不仅仅出现于对于某一个泛型的类型特化,也可以是对于某个具体类型的值特化。

​ 回到 enable_if 的实现,enable_if 结构体的主模板定义为:

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

主模板并没有定义任何东西,这时对于第一个bool值进行值特化:

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

因此当出现 enable_if<true, A> 之类的类型时,编译器会根据特化选择下面的定义,因此可以通过 type 获取类型T。当bool值非true时,根据SFIANE原则,获取type值所在的函数或类型不会被编译,因此实现了根据bool值来决定类/函数是否编译的效果。