摘要訊息 : C++ 14 Proposal N3651《Variable Templates》導讀

C++ 14 Proposal N3651《Variable Templates》導讀

程式設計師都是懶惰的人, 否則電腦將毫無用處. 在 C++ 11 中, 如果我們想要獲得某個特性萃取的結果, 我們通常會有這樣的程式碼 :

constexpr auto value {is_signed<int>::value};

對於某個固定的常數, 我們只能寫出這樣的程式碼 :

constexpr auto value {0};

如果在編寫程式的時候, 我們並不知道變數的型別, 那麼我們會寫一個函式 :

template <typename T>

T value() {

    return 0;

}

而這篇 Proposal 提出了為 C++ 增加變數樣板. 變數樣板的基本形式是變數和函式樣板的中間體 :

template <樣板參數>

[constexpr] [static] [inline] T VARIABLE_NAME {初始化列表};

其中, 帶有 "[]" 的都是可選的, T 表示變數的型別

例如, \pi\doteq 3.1415926, 但是 3.1415926 這一常數在 C++ 中, 如果沒有任何後綴, 那麼預設是 double 型別. 而實際使用的時候, 它並不一定是 double 型別, 它可以是 long double, 可以是 float, 甚至可以是用戶自訂型別. 如果沒有辦法確定它的型別, 但是又要用到它, 那麼這篇 Proposal 提出的變數樣板的寫法將會非常簡便 :

template <typename T>

constexpr T pi {3.1415926};

回到我們最初的問題, 我們希望簡化陳述式 constexpr auto value {is_signed<int>::value}; 運用變數樣板, 可以將其寫成 :

#include <type_traits>

using namespace std;

template <typename T>

constexpr auto is_signed_value {is_signed<T>::value};

那麼接下來, 我們就無需再使用 is_signed<T>::value 這樣的使用方式, 直接使用 is_signed_value<T> 即可. 是不是寫少了一些呢?

對於類別內的 static 變數, 同樣也可以使用樣板變數. 但是如果初始化在類別可視範圍之外, 那麼需要重新宣告樣板參數. 另外, 如果變數型別和樣板參數無關, 也就是變數的型別已知, 那麼變數必須在類別可視範圍內進行初始化 :

#include <iostream>

using namespace std;

struct Foo {

    template <typename T>

    static const volatile T value;

};

template <typename T>

const volatile T Foo::value {};



struct Bar {

    template <typename T>

    constexpr static bool value;        //Error!

    template <typename T>

    constexpr static bool value {is_union<T>::value};       //OK

};

另外, 如果你熟悉 SFNIAE, 那麼需要注意的是, SFINAE 並不能用於變數樣板中 :

#include <iostream>



using namespace std;



template <int N>

typename enable_if<N % 2 == 0, int>::type value {};



int main(int argc, char *argv[]) {

    cout << value<0> << endl;       //輸出結果 : 0

    cout << value<1> << endl;       //Error : failed requirement '1 % 2 == 0'; 'enable_if' cannot be used to disable this 
declaration

}

一旦編碼器檢測到 enable_if 的第一個樣板引數的結果為 false, 那麼就會立馬擲出編碼錯誤. 另外, 宣告兩個帶有 SFINAE 的 value 看似可行, 但是這會導致編碼錯誤 :

template <int N>

typename enable_if<N % 2 == 0, int>::type value {};

template <int N>

typename enable_if<N % 2 == 1, char>::type value {'0'};     //redefinition of 'value' with a different type: 'typename enable_if<N % 2 == 1, char>::type' vs 'typename enable_if<N % 2 == 0, int>::type'

因此, SFINAE 並不可以被用於樣板參數 (其實, 讓變數樣板支援 SFINAE 的可行性已經被討論過了, 不過到 C++ 2a 為止, 它並沒有被採納)