C++ 14变量模板:它们的目的是什么?任何用法示例?

jbg*_*bgs 52 c++ templates rationale c++14

C++ 14将允许创建模板化的变量.通常的例子是变量'pi',可以读取它以获得各种类型的数学常数π的值(3表示int;可能的最接近的值float等)

除此之外,我们可以通过将变量包装在模板化的结构或类中来实现此功能,这与类型转换如何混合?我看到一些重叠.

除了pi示例之外,它如何与非const变量一起使用?任何用法示例,以了解如何充分利用此类功能及其目的是什么?

小智 26

除了pi示例之外,它如何与非const变量一起使用?

目前,它似乎分别为类型实例化变量.即,您可以指定10 n<int>,它将与模板定义不同.

template<typename T>
T n = T(5);

int main()
{
    n<int> = 10;
    std::cout << n<int> << " ";    // 10
    std::cout << n<double> << " "; // 5
}
Run Code Online (Sandbox Code Playgroud)

如果声明是const,它只是readonly.如果它是a constexpr,就像所有constexpr声明一样,它在外面没有多少用处constexpr(ressions).

除此之外,我们可以通过将变量包装在模板化的结构或类中来实现此功能,这与类型转换如何混合?

这是一个简单的提案.我无法看到它如何以显着的方式影响类型转换.正如我已经说过的,变量的类型是您实例化模板的类型.即,decltype(n<int>)是int.decltype((double)n<int>)是两倍,等等.

任何用法示例,以了解如何充分利用此类功能及其目的是什么?

N3651提供了简洁的理由.

唉,现有的C++规则不允许模板声明声明变量.这个问题有众所周知的解决方法:

•使用类模板的constexpr静态数据成员

•使用constexpr函数模板返回所需的值

这些变通方法已有数十年的历史,并且已有详细记载.诸如std :: numeric_limits之类的标准类是典型的例子.虽然这些变通方法并不完美,但它们的缺点在某种程度上是可以容忍的,因为在C++ 03时代,只有简单的内置类型常量才能享受不受约束的直接和高效的编译时支持.所有这一切都随着在C++ 11中采用constexpr变量而改变,这扩展了对用户定义类型常量的直接和有效支持.现在,程序员正在使程序中的常量(类类型)越来越明显.因此,增加与变通方法相关的混乱和挫折感.

...

"静态数据成员"的主要问题是:

•它们需要"重复"声明:一旦进入类模板,一旦在类模板之外,提供"真实"定义,以防常常使用常量.

•程序员对提供两次相同声明的必要性感到困惑和困惑.相比之下,"普通"常量声明不需要重复声明.

...

此类别中众所周知的示例可能是numeric_limits的静态成员函数,或者 boost::constants::pi<T>()诸如此类的函数等.Constexpr函数模板不会遇到静态数据成员具有的"重复声明"问题; 此外,它们提供功能抽象.但是,它们迫使程序员在定义站点提前选择如何传递常量:通过const引用或通过普通的非引用类型.如果通过const引用传递,那么必须在静态存储中系统地分配常量; 如果是非引用类型,那么常量需要复制.复制不是内置类型的问题,但它是用户定义类型的showstopper,其值语义不仅仅是微内置类型的包装(例如矩阵,整数或大浮点数等).相比之下,"普通的"const(expr)变量不会遇到这个问题.提供了一个简单的定义,并且确定常量是否实际需要在存储中布局仅取决于用法而不是定义.


n. *_* m. 24

我们可以通过在模板化的结构或类中包装变量来实现此功能

是的,但这将是无偿的句法盐.对血压不健康.

pi<double>更好地传达意图pi<double>::value.精炼到位.这完全是我书中允许和鼓励这种语法的原因.


Lev*_*son 8

C++ 14变量模板的另一个实际例子是当你需要一个函数来传递一些东西时std::accumulate:

template<typename T>
T const & (*maxer) (T const &, T const &) = std::max<T>;

std::accumulate(some.begin(), some.end(), initial, maxer<float>);
Run Code Online (Sandbox Code Playgroud)

请注意,使用std::max<T>不足是因为它无法推断出确切的签名.在这个特定的例子中,您可以使用max_element,但重点是有一整类函数共享此行为.


Lau*_*ZZA 6

我想知道这些方面是否可行:(假设模板lambdas的可用性)

void some_func() {
    template<typename T>
    std::map<int, T> storage;

    auto store = []<typename T>(int key, const T& value) { storage<T>[key] = value; };

    store(0, 2);
    store(1, "Hello"s);
    store(2, 0.7);

    // All three values are stored in a different map, according to their type. 
}
Run Code Online (Sandbox Code Playgroud)

现在,这有用吗?

作为一个更简单的用法,请注意初始化pi<T>使用显式转换(显式调用一元构造函数)而不是统一初始化.这意味着,给定radians带有构造函数的类型radians(double),您可以编写pi<radians>.


cma*_*ter 5

好吧,您可以使用它来编写如下编译时代码:

#include <iostream>

template <int N> const int ctSquare = N*N;

int main() {
    std::cout << ctSquare<7> << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

与同等产品相比,这是一个显着的改进

#include <iostream>

template <int N> struct ctSquare {
    static const int value = N*N;
};

int main() {
    std::cout << ctSquare<7>::value << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

在引入变量模板之前,人们常常编写来执行模板元编程。对于非类型值,从 C++11 开始我们就可以使用 来做到这一点constexpr,因此模板变量的优点只是允许基于变量模板的类型进行计算。

TL;DR:它们不允许我们做任何我们以前做不到的事情,但它们使模板元编程不再是 PITA。