既然允许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;
}