浅墨浓香

想要天亮进城,就得天黑赶路。

导航

第5章 技巧性基础:5.6 变量模板

Posted on 2020-04-22 00:46  浅墨浓香  阅读(776)  评论(0编辑  收藏  举报

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
*/