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