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 << ,這樣程序在編譯的時候不會報錯,程序運行的過程中才去判斷是否打印。原作者這樣寫,可能是考慮到將是否打印的判斷放到了編譯階段,提高運行的效率

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