C++14 变量模板:它们的目的是什么?任何使用示例?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21051141/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++14 Variable Templates: what is their purpose? Any usage example?
提问by jbgs
C++14 will allow the creation of variables that are templated. The usual example is a variable 'pi' that can be read to get the value of the mathematical constant π for various types (3 for int
; the closest value possible with float
, etc.)
C++14 将允许创建模板化的变量。通常的例子是一个变量“pi”,它可以被读取以获取各种类型的数学常数 π 的值(3 代表int
;可能与 最接近的值float
,等等)
Besides that we can have this feature just by wrapping a variable within a templated struct or class, how does this mix with type conversions? I see some overlapping.
除此之外,我们可以通过将变量包装在模板结构或类中来拥有此功能,这如何与类型转换混合?我看到有些重叠。
And other than the pi example, how would it work with non-const variables? Any usage example to understand how to make the most of such a feature and what its purpose is?
除了 pi 示例之外,它如何与非常量变量一起工作?任何使用示例来了解如何充分利用此类功能及其目的是什么?
采纳答案by jbgs
And other than the pi example, how would it work with non-const variables?
除了 pi 示例之外,它如何与非常量变量一起工作?
Currently, it seems to instantiate the variables separately for the type. i.e., you could assign 10 to n<int>
and it would be different from the template definition.
目前,它似乎为类型单独实例化变量。即,您可以将 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
}
If the declaration is const
, it is readonly. If it's a constexpr
, like all constexpr
declarations, it has not much use outside constexpr
(ressions).
如果声明是const
,则它是只读的。如果它是 a constexpr
,就像所有constexpr
声明一样,它在外部constexpr
(resions)没有多大用处。
Besides that we can have this feature just by wrapping a variable within a templated struct or class, how does this mix with type conversions?
除此之外,我们可以通过将变量包装在模板结构或类中来拥有此功能,这如何与类型转换混合?
It's meant to be a simple proposal. I am unable to see how it affects type conversions in a significant way. As I already stated, the type of the variable is the type you instantiated the template with. i.e., decltype(n<int>)
is int. decltype((double)n<int>)
is double and so on.
这意味着一个简单的建议。我无法看到它如何以重要的方式影响类型转换。正如我已经说过的,变量的类型是您实例化模板所用的类型。即,decltype(n<int>)
是 int。decltype((double)n<int>)
是双等。
Any usage example to understand how to make the most of such a feature and what its purpose is?
任何使用示例来了解如何充分利用此类功能及其目的是什么?
N3651provides a succinct rationale.
N3651提供了一个简洁的原理。
Alas, existing C++ rules do not allow a template declaration to declare a variable. There are well known workarounds for this problem:
? use constexpr static data members of class templates
? use constexpr function templates returning the desired values
These workarounds have been known for decades and well documented. Standard classes such as std::numeric_limits are archetypical examples. Although these workarounds aren't perfect, their drawbacks were tolerable to some degree because in the C++03 era only simple, builtin types constants enjoyed unfettered direct and efficient compile time support. All of that changed with the adoption of constexpr variables in C++11, which extended the direct and efficient support to constants of user-defined types. Now, programmers are making constants (of class types) more and more apparent in programs. So grow the confusion and frustrations associated with the workarounds.
唉,现有的 C++ 规则不允许模板声明来声明变量。这个问题有一些众所周知的解决方法:
? 使用类模板的 constexpr 静态数据成员
? 使用 constexpr 函数模板返回所需的值
这些变通方法已经为人所知数十年并且有据可查。std::numeric_limits 等标准类是典型的例子。尽管这些变通方法并不完美,但它们的缺点在某种程度上是可以容忍的,因为在 C++03 时代,只有简单的内置类型常量才能享受不受约束的直接和高效的编译时支持。所有这一切都随着 C++11 中 constexpr 变量的采用而改变,它扩展了对用户定义类型的常量的直接和有效支持。现在,程序员使(类类型的)常量在程序中越来越明显。因此,与变通方法相关的困惑和挫折越来越大。
...
...
The main problems with "static data member" are:
? they require "duplicate" declarations: once inside the class template, once outside the class template to provide the "real" definition in case the con- stants is odr-used.
? programmers are both miffed and confused by the necessity of providing twice the same declaration. By contrast, "ordinary" constant declarations do not need duplicate declarations.
“静态数据成员”的主要问题是:
? 它们需要“重复”声明:一次在类模板内,一次在类模板外,以提供“真实”定义,以防常量被 odr 使用。
? 程序员对提供两次相同声明的必要性既恼火又困惑。相比之下,“普通”常量声明不需要重复声明。
...
...
Well known examples in this category are probably static member functions of numeric_limits, or functions such as
boost::constants::pi<T>()
, etc. Constexpr functions templates do not suffer the "duplicate declarations" issue that static data members have; furthermore, they provide functional abstraction. However, they force the programmer to chose in advance, at the definition site, how the constants are to be delivered: either by a const reference, or by plain non- reference type. If delivered by const reference then the constants must be systematically be allocated in static storage; if by non-reference type, then the constants need copying. Copying isn't an issue for builtin types, but it is a showstopper for user-defined types with value semantics that aren't just wrappers around tiny builtin types (e.g. matrix, or integer, or bigfloat, etc.) By contrast, "ordinary" const(expr) variables do not suffer from this problem. A simple definition is provided, and the decision of whether the constants actually needs to be layout out in storage only depends on the usage, not the definition.
这个类别中众所周知的例子可能是 numeric_limits 的静态成员函数,或者诸如
boost::constants::pi<T>()
Constexpr 函数模板不会遇到静态数据成员所具有的“重复声明”问题;此外,它们提供功能抽象。然而,它们迫使程序员在定义站点提前选择常量的传递方式:通过常量引用或通过普通的非引用类型。如果通过常量引用传递,那么常量必须系统地分配到静态存储中;如果是非引用类型,则常量需要复制。复制对于内置类型来说不是问题,但对于具有值语义的用户定义类型来说,它是一个阻碍,而不仅仅是对微小内置类型(例如矩阵、整数或 bigfloat 等)的包装。相比之下,“普通” const(expr) 变量不会遇到这个问题。
回答by n. 'pronouns' m.
we can have this feature just by wrapping a variable within a templated struct or class
我们只需将变量包装在模板结构或类中即可拥有此功能
Yes, but that would be gratuitous syntactic salt. Not healthy for the blood pressure.
是的,但这将是无偿的语法盐。对血压不健康。
pi<double>
conveys the intent better than pi<double>::value
. Short and to the point. That's enough of a reason in my book to allow and encourage this syntax.
pi<double>
比 更能传达意图pi<double>::value
。精炼到位。在我的书中,这足以成为允许和鼓励这种语法的理由。
回答by Levi Morrison
Another practical example for C++14's variable templates is when you need a function for passing something into std::accumulate
:
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>);
Note that using std::max<T>
is insufficient because it can't deduce the exact signature. In this particular example you can use max_element
instead, but the point is that there is a whole class of functions that share this behavior.
请注意, usingstd::max<T>
是不够的,因为它无法推导出确切的签名。在这个特定的例子中,你可以max_element
改用,但重点是有一整类函数共享这种行为。
回答by Laurent LA RIZZA
I wonder whether something along these lines would be possible: (assuming availability of template lambdas)
我想知道是否有可能发生这些事情:(假设模板 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.
}
Now, is this useful?
现在,这有用吗?
As a simpler use, notice that the initialization of pi<T>
uses explicit conversion (explicit call of a unary constructor) and not uniform initialization. Which means that, given a type radians
with a constructor radians(double)
, you can write pi<radians>
.
作为更简单的使用,请注意初始化pi<T>
使用显式转换(一元构造函数的显式调用)而不是统一初始化。这意味着,给定radians
具有构造函数的类型radians(double)
,您可以编写pi<radians>
.
回答by cmaster - reinstate monica
Well, you can use this to write compile time code like this:
好吧,您可以使用它来编写这样的编译时代码:
#include <iostream>
template <int N> const int ctSquare = N*N;
int main() {
std::cout << ctSquare<7> << std::endl;
}
This is a significant improvement over the equivalent
这是对等价物的显着改进
#include <iostream>
template <int N> struct ctSquare {
static const int value = N*N;
};
int main() {
std::cout << ctSquare<7>::value << std::endl;
}
that people used to write to perform template metaprogramming before variable templates were introduced. For non-type values, we were able to do this since C++11 with constexpr
, so template variables have only the advantage of allowing computations based on types to the variable templates.
在引入变量模板之前,人们过去常常编写来执行模板元编程。对于非类型值,从 C++11 开始,我们就可以使用constexpr
,因此模板变量的优点是允许基于类型对变量模板进行计算。
TL;DR: They don't allow us to do anything we couldn't do before, but they make template metaprogramming less of a PITA.
TL;DR:他们不允许我们做任何我们以前不能做的事情,但他们使模板元编程不再是 PITA。
回答by Tiger Hwang
I have a use case here.
我这里有一个用例。
template<typename CT> constexpr CT MARK = '%';
template<> constexpr wchar_t MARK<wchar_t> = L'%';
which are used in a string processing template.`
用于字符串处理模板。`
template <typename CT>
void ProcessString(const std::basic_string<CT>& str)
{
auto&& markpos = str.find(MARK<CT>);
...
}