C++基礎::變數模板(variable template)

Inside_Zhang發表於2015-11-14

既然允許C++模板類(class template)的存在,允許C++函式模板(function template)的存在,也應當允許變數模板(variable template)的存在。

引入

變數模板,標準C++14(C++11是C++14的一個subset子集,)的一個新的語法特性。C++新標準引入變數模板的主要目的是為了簡化定義(simplify definitions)以及對模板化常量(parameterized constant)的支援

C++14之前的語法規則不允許使用模板宣告的方式宣告一個變數

template<typename T>
T var;              // not allowed in pre-C++14
var<int> = 5;           // == (int var = 5;)

它足夠新,以至於相當多的編譯器還未完全支援,比如windows下的vs2013。本文涉及的程式碼統一在C++的一個線上ide——ideone中進行編譯(選擇這個編譯器的目的主要在於它對C++14標準的支援)。


這裡寫圖片描述
線上編譯器c++標準可選

條條大陸通羅馬,此路不通(C++14標準之前),我們雖然有一些另外的替代方案,但這些方案,往往冗餘度過高且形式比較複雜。

workaround 1

第一種替代方案是,使用類别範本的constexpr static資料成員的方式:

template<typename T>
struct PI
{
    constexpr static T pi = T(3.1415926535897932385);
    // 這裡必須使用關鍵字constexpr,而不可以是const
    // const 常量必須在編譯器得到確定
    // 自C++11起,constexpr可以讓表示式核定於編譯期
}

// duplicate declaration
template<typename T>
constexpr T PI<T>::pi;

int main(int, char**)
{   
    std::cout << PI<int>::pi << std::endl;  
                                        // 3
    std::cout << PI<double>::pi << std::endl;   
                                        // 3.14159
    return 0;
}

這種做法,因為constant是一種ODR(One Definition Rule)的定義規則。對constant的兩次宣告是有必要的,一次在類别範本體內,一次在類别範本體外,如上程式碼所示。

workaround 2

另外一種解決方案是使用constexpr函式模板的形式,該函式返回期待的型別。

template<typename T>
T PI()
{
    constexpr T pi = T(3.1415926535897932385);
    return pi;
}

int main(int, char**)
{
    std::cout << PI<int>() << std::endl;
    std::cout << PI<double>() << std::endl;
    return 0;
}

variable template

現在,有了C++新標準對變數模板的支援,操縱一個可變型別的常量可以得到顯著的簡化,

// old version
PI<int>::pi     // constexpr static data member
PI<int>()       // constexpr function

// new version
PI<int>
template<typename T>
constexpr T PI = T(3.1415926535897932385);
template<typename T>
T area(T r)
{
    return PI<T>*r*r;
}

int main(int, char**)
{
    std::cout << PI<int> << std::endl;
    std::cout << area(2.0) << std::endl;    
                // 函式模板的自動型別推導
    return 0;
}

當然我們亦可以將變數模板應用到一個非常量變數上來;

tempalte<typename T>
T val = T(3.1415926535897932385);

int main(int, char**)
{
    val<float> = 0.6180339887498948482;
    std::cout << val<float> << std::endl;   
                // 使用新賦的值
    std::cout << val<double> << std::endl;
                // 使用預設值
    return 0;
}

References

[1] <Introduction to Variable Templates of C++14>

相關文章