C + + 14变量模板: 它们的用途是什么? 有什么用法示例吗?

C + + 14将允许创建模板化的变量。通常的例子是一个可以读取的变量‘ pi’,以获得不同类型的数学常数 π 的值(int为3; float等最接近的值)

除此之外,我们还可以通过在模板结构或类中包装变量来实现这一特性,这与类型转换有什么关系呢?我看到了一些重叠。

除了 pi的例子,它如何与非 const变量一起工作?是否有使用例子来理解如何最大限度地利用这样一个特性以及它的目的是什么?

46599 次浏览

我想知道沿着这些思路是否可行: (假设模板 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.
}

这个有用吗?

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

我们可以通过在模板结构或类中包装变量来实现这个特性

是的,但那是无意义的句法盐,对血压不好。

pi<double>pi<double>::value更好地传达意图。言简意赅。在我的书中,这个理由足以支持并鼓励这种语法。

除了圆周率的例子,它如何与非常量 变数?

目前,它似乎是为类型单独实例化变量。也就是说,您可以将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
}

如果声明为 const,则为 readonly。如果它是一个 constexpr,就像所有的 constexpr声明一样,它在 constexpr(ressions)之外没有多少用处。

除此之外,我们可以通过包装一个变量来实现这个特性 在模板化的结构或类中,这如何与 转换?

这应该是个简单的求婚。我无法看到它是如何显著地影响类型转换的。如前所述,变量的类型是您用来实例化模板的类型。也就是说,decltype(n<int>)是 int。decltype((double)n<int>)是双倍的,以此类推。

任何理解如何充分利用这种特性的使用示例 它的目的是什么?

N3651 提供了一个简洁的理由。

遗憾的是,现有的 C + + 规则不允许模板声明 声明一个变量 问题:

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

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

几十年来,这些变通方法已经为人所知,并得到了充分的记录。 诸如 std: : numeric _ limit 之类的标准类是典型的 虽然这些变通方法并不完美,但它们的缺点 在某种程度上是可以忍受的,因为在 C + + 03时代只有简单, 内建类型常量享有不受约束的直接和高效 编译时支持。所有这些都随着采用 在 C + + 11中扩展了直接有效的 conexpr 变量 支持用户定义类型的常量 使(类类型的)常量在程序中越来越明显。 因此,增长的困惑和挫折相关的 变通办法。

...

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

•它们需要“重复”声明: 一旦进入类 模板,一旦外面的类模板提供了“真正的” 定义,以防常量被用于。

•程序员们对提供两倍于此的服务的必要性感到恼火和困惑 相比之下,“普通”常量声明不需要 重复声明。

...

此类别中的著名示例可能是静态成员 函数,或者诸如 等等。 Constexpr 函数模板没有 遭受静态数据成员的“重复声明”问题 此外,它们还提供了函数抽象。然而,它们 强迫程序员在定义站点预先选择,如何 传递常量: 或者通过常量引用,或者通过 普通非引用类型。如果通过常量引用传递,则 必须在静态存储中系统地分配常数; 如果 通过非引用类型,则常量需要复制 内置类型的问题,但它是一个用户定义的展示停止 值语义的类型,而不仅仅是 Tiny 的包装器 内建类型(例如矩阵、整数或 bigfloat 等) 相比之下,“普通”const (expr)变量不受此影响 提供了一个简单的定义,并决定 常量是否实际上只需要在存储中进行布局 取决于用法,而不是定义。

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>);

注意,使用 std::max<T>是不够的,因为它不能推导出确切的签名。在这个特殊的示例中,您可以使用 max_element,但是关键是有一整类函数共享这种行为。

你可以用它来编写这样的编译时代码:

#include <iostream>


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


int main() {
std::cout << ctSquare<7> << std::endl;
}

这是一个重大的改进,相对于同等的

#include <iostream>


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


int main() {
std::cout << ctSquare<7>::value << std::endl;
}

在引入变量模板之前,人们用它来执行模板超编程。对于非类型值,我们可以在 C + + 11中使用 constexpr来实现这一点,因此模板变量只有允许基于类型对变量模板进行计算的优势。

他们不允许我们做以前不能做的事情,但是他们让模板超编程变得更少。

我这里有一个用例。

template<typename CT> constexpr CT MARK = '%';
template<> constexpr wchar_t MARK<wchar_t> = L'%';

用于字符串处理模板

template <typename CT>
void ProcessString(const std::basic_string<CT>& str)
{
auto&& markpos = str.find(MARK<CT>);
...
}