enable_if 的一个奇怪用法

std::enable_if的理解

引出问题

查看同事写的代码时,有一段代码自己理解了很长时间,在这里记录一下。下面的代码片段是我简化后的部分

#include <iostream>
using namespace std;

class Father {
public:
    void print(ostream &os) { os << "Father" << endl; }
    enum { E_PRINT_ENABLE = 1, };
};

class Son : public Father {
public:
    void print(ostream &os) { os << "Son" << endl; }
    enum { E_PRINT_ENABLE = 0, };
};

template<class T, class = enable_if<T::E_PRINT_ENABLE>::type>
ostream & operator << (ostream &os, T & t) {
    t.print(os);
    return os;
}

int main()
{
    Father cFather;
    cout << cFather << endl;

    // compile error C2679
    Son cSon;
    cout << cSon << endl;
} 

当时主要是以下几点不太明白,后面的部分会着重介绍
- enable_if 的使用
- typename 的作用
- 模板函数operator << 中第二个模板类型参数使用 class = 的含义

enable_if 使用

  enable_if 的介绍参考 http://en.cppreference.com/w/cpp/types/enable_if。具体的源码实现也很简单,通过模板和模板的具体化来实现的,简单的来说就是当模板参数B为true时定义一个类型type(它的类型为T)

// #1
template<bool B, class T = void>
struct enable_if {};

// #2 
template<class T>
struct enable_if<true, T> { typedef T type; };

  关于模板的实例化和具体化相关的信息参考另外一篇文章 C++模板的具体化和实例化

operator << 的实现原理

  operator << 从实现上来看是重载了 << 运算符,使得下面的语句符合语法。

Father cFather;
cout << cFather << endl;

  与普通的重载 << 运算符相比,这里唯一的不同是使用了模板,模板的第一个类型参数还好理解,第二个类型参数 class = enable_if::type 其实等价于 class L = enable_if::type,因为在函数的实现过程中没有使用到这个类型参数,所以直接不写类型变量的名称。下面就引出了两个问题

第二个类型参数到底是什么类型

  • 如果 T::E_PRINT_ENABLE 为 false,根据模板(#1) 的实现,它是没类型的
  • 如果 T::E_PRINT_ENABLE 为 ture 时按照模板的具体化(#2)来实现的,class = enable_if::type 的类型应该是模板具体化(#2)中的第二个类型参数,如果没有确定第二个类型参数的话,会使用模板定义(#1)中的默认参数,所以当 T::E_PRINT_ENABLE 为 true 时,返回的类型是 void

为什么要加上第二个类型参数

  在函数的实现过程中根本不需要第二个类型参数,这里添加第二个类型参数主要是想限制 operator << 只对 Father 类对象有效,对 Son 类对象无效。

  Father 类对象中 T::E_PRINT_ENABLE 为 1,oparot << 实例化的时候,第一个类型参数的类型为 Father,第二个类型参数中 enable_if::type 为void,既第二个类型参数默认为void。所以#1部分的代码可以正常编译通过

  Son 类对象中 T::E_PRINT_ENABLE 为 0,oparot << 实例化的时候,第一个类型参数的类型为 Son,第二个类型参数中 enable_if::type 没有值,还需要传入第二个类型参数的类型后才能正常实例化。所以#2部分的代码不能正常编译通过

后记

  其实完全可以使用一个成员变量来控制 operator << ,这样程序在编译的时候不会报错,程序运行的过程中才去判断是否打印。原作者这样写,可能是考虑到将是否打印的判断放到了编译阶段,提高运行的效率

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章