5.6 Variable Templates
5.6 变量模板
Since C++14, variables also can be parameterized by a specific type. Such a thing is called a variable template.
从C++14开始,变量也可以通过特定类型进行参数化。这被称为变量模板。
For example, you can use the following code to define the value of ˇ while still not defining the type of the value:
例如,你可以使用下面的代码来定义pi的值,但其pi的类型尚未定义。
template<typename T> constexpr T pi{3.1415926535897932385};
Note that, as for all templates, this declaration may not occur inside functions or block scope.
注意,对于所有的模板,这段声明都不能出现在函数或块作用域内部。
To use a variable template, you have to specify its type. For example, the following code uses two different variables of the scope where pi<> is declared:
要使用变量模板,必须指定其类型。例如,以下代码使用了两个不同的变量,其作用域是声明pi<>模板所在的作用域(译注:即constexpr T pi{3.1415926535897932385};这行语句所属的作用域,而不是实例化pi<double>或pi<float>那行代码所属的作用域!这点很重要!)
std::cout << pi<double> << '\n'; std::cout << pi<float> << '\n’;
You can also declare variable templates that are used in different translation units:
你也可以声明在不同翻译单元中使用的变量模板
//== header.hpp: template<typename T> T val{}; // 零初始化val
//== 翻译单元 1: #include "header.hpp" int main() { val<long> = 42; print(); }
//== 翻译单元 2: #include "header.hpp" void print() { std::cout << val<long> << '\n'; // OK: prints 42(译注:此处val<long>为全局变量!) }
Variable templates can also have default template arguments:
变量模板也可以具有默认模板实参
template<typename T = long double> constexpr T pi = T{3.1415926535897932385};
You can use the default or any other type:
可以使用默认或任何其它类型
std::cout << pi<> << ‘\n’; //输出long double类型 std::cout << pi<float> << ‘\n’; //输出float类型
However, note that you always have to specify the angle brackets. Just using pi is an error:
但是,请注意,你总应该指定尖括号。仅使用pi是错误的:
std::cout << pi << ‘\n’; //ERROR
Variable templates can also be parameterized by nontype parameters, which also may be used to parameterize the initializer. For example:
可以用非类型参数对变量模板进行参数化, 也可以将非类型参数用于初始化器(initializer)的参数化。例如:
#include <iostream> #include <array> template<int N> std::array<int, N> arr{}; // N个元素的数组,零初始化(译注,使用initializer进行初始化) template<auto N> constexpr decltype(N) dval = N; // dval的类型依赖于传入的值 int main() { std::cout << dval<'c'> << '\n'; // N 为char型的 'c'值。 arr<10>[0] = 42; // 设置全局arr第1个元素的值 for (std::size_t i = 0; i < arr<10>.size(); ++i) { // 使用arr中的值 std::cout << arr<10>[i] << '\n'; } }
Again, note that even when the initialization of and iteration over arr happens in different translation units the same variable std::array<int,10> arr of global scope is still used.
请再注意一下,即使arr的初始化和迭代发生在不同的翻译单元中,也仍然使用的是全局作用域中的同一个变量(std::array<int,10> arr)。
Variable Templates for Data Members
数据成员的变量模板
A useful application of variable templates is to define variables that represent members of class templates. For example, if a class template is defined as follows:
变量模板一个很有用的应用就是定义一些表示类模板成员的变量。例如,如果类模板定义如下:
template<typename T> class MyClass { public: static constexpr int max = 1000; };
which allows you to define different values for different specializations of MyClass<>, then you can define
它允许你为MyClass<>的不同特化版本定义不同的值,然后你可以定义
template<typename T>
int myMax = MyClass<T>::max;
so that application programmers can just write
因此,应用程序员可以就可以编写:
auto i = myMax<std::string>;
instead of
来替换
auto i = MyClass<std::string>::max;
This means, for a standard class such as
这意味着,对于一个标准的类,例如:
namespace std { template<typename T> class numeric_limits { public: ... static constexpr bool is_signed = false; ... }; }
you can define
可以定义
template<typename T> constexpr bool isSigned = std::numeric_limits<T>::is_signed;
to be able to write
就能编写
isSigned<char>
instead of
来替换
std::numeric_limits<char>::is_signed
Type Traits Suffix _v
类型萃取后缀_v
Since C++17, the standard library uses the technique of variable templates to define shortcuts for all type traits in the standard library that yield a (Boolean) value. For example, to be able to write
从C++17起,标准库使用变量模板技术,来为库中所有(布尔) 值的类型萃取定义一个快捷方式。例如,为了能用
std::is_const_v<T> // since C++17
instead of
来替换
std::is_const<T>::value //since C++11
the standard library defines
标准库定义了
namespace std { template<typename T> constexpr bool is_const_v = is_const<T>::value; }
【编程实验】变量模板及作用域
#include<iostream> using namespace std; //变量模板 template<typename T> T var; //声明变量模板,由于在全局作用域下声明,这也意味着 //无论是用哪种类型实例化var ,这些对象都属于全局作用域 void test_assign() //为变量模板赋值 { //以下变量的作用域属全局,而不是该函数! //因此,可以在其他函数内使用它们 var<int> = 7; var<double> = 3.14; var<char> = 'a'; } void test_print() { std::cout << "var<int> = " << var<int> << ", address:" << &var<int> << std::endl; std::cout << "var<double> = " << var<double> << ", address:" << &var<double> << std::endl; std::cout << "var<char> = " << var<char> << ", address:" << &var<char> << std::endl; } //C++14之前,为了达到定义变量模板的效果,可用类模板或函数模板来替代,但比较复杂 //替代方案1:使用类模板 template<typename T> struct VAR { static T var; //此处为声明,需在类外定义 }; template<typename T> T VAR<T>::var; void test_VAR() { VAR<int>::var = 10; VAR<double>::var = 3.14; std::cout << "VAR<int> = " << VAR<int>::var << std::endl; std::cout << "VAR<double> = " << VAR<double>::var << std::endl; } //替代方案2:使用函数模板 template<typename T> T PI() { constexpr T pi = T(3.1415926); return pi; } void test_pi() { std::cout << "PI<int> = " << PI<int>() << std::endl; std::cout << "PI<double> = " << PI<double>() << std::endl; } //min的作用域:limist类作用域 struct limits { template<typename T> static T min; //静态成员变量,变量模板 }; template<typename T> T limits::min = {}; //定义min void test_min() { limits::min<int> = 3; limits::min<double> = 3.14; std::cout <<"limits::min<int> = " << limits::min<int> << std::endl; std::cout << "limits::min<double> = " << limits::min<double> << std::endl; } int main() { //全局作用域的变量模板 test_assign(); test_print(); test_VAR(); test_pi(); //类作用域的变量模板 test_min(); } /*输出结果: var<int> = 7, address:00B2C138 var<double> = 3.14, address:00B2C148 var<char> = a, address:a VAR<int> = 10 VAR<double> = 3.14 PI<int> = 3 PI<double> = 3.14159 limits::min<int> = 3 limits::min<double> = 3.14 */