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