模板参数包扩展语法的理由
Rationale of template parameter pack expansion syntax
给定以下辅助功能
template <typename ... Ts>
auto f(Ts&& ... args) {}
template <typename T>
auto g(T x) { return x; }
1)我们照常展开模板参数包。
template <typename ... Ts>
void test1(Ts&& ... args)
{
f(args...);
}
2)在这里扩展...
发生在g()
的功能调用之后。这也是合理的,因为每个args
都被调用g()
:
template <typename ... Ts>
void test2(Ts&& ... args)
{
f(g(args)...);
}
3)具有相同的逻辑,我期望test3(Is, args...)...
,但没有。您必须写test3(Is..., args...)
:
template <typename ... Ts>
void test3(size_t i, Ts&& ... args)
{
f(args...);
}
template <typename ... Ts>
void test3(std::index_sequence<Is...>, Ts&& ... args)
{
// would expect test3(Is, args...)...;
test3(Is..., args...);
}
我知道,我使用它,但是,我不明白。模板扩展的整个概念是表达折叠的一种形式。不是以C 17方式,而是从...
之前的子表达(如果愿意)折叠(或重复),相对于variadic参数。在test3
的情况下,我们将test3(Is, args...)
的表达式CC_9"折叠"。但是我们必须编写test3(Is..., args...)
而不是test3(Is, args...)...
。
使用标准的这种怪异逻辑,您也可以编写f(g(args...))
而不是f(g(args)...)
-但这是无效的。看来该语言在不同的上下文中使用不同的逻辑。
不同语法背后的理由是什么?
在
test3
情况下,我们正在"折叠"Is
的表达式test3(Is, args...)
。但是我们必须编写test3(Is..., args...)
而不是test3(Is, args...)....
这实际上是不正确的。test3(Is..., args...)
将将Is
扩展到位,然后将args
扩展到位。因此,呼叫test3(index_sequence<0,1,2>, x, y, z)
最终将调用test3(0, 1, 2, x, y, z)
,这不是您想发生的事情。您想要test3(0, x, y, z); test3(1, x, y, z); test3(2, x, y, z);
。
C 17调用的方法是:
(test3(Is, args...), ...);
这并不是真正不同的语法。您有两个参数包,要以不同的方式扩展:args
在函数调用中和围绕它的Is
中,这意味着您有两个...
s。逗号只是一种表明这些单独语句的方式。
...
放置中的自由意味着您无论如何都可以折叠它:
(test3(Is, args), ...); // test3(0,x); test3(1,y); test3(2,z);
(test3(Is..., args), ...); // test3(0,1,2,x); test3(0,1,2,y); test3(0,1,2,z);
test3(Is..., args...); // test3(0,1,2,x,y,z);
使用标准的这种怪异逻辑,您也可以编写
f(g(args...))
而不是f(g(args)...)
- 但这是无效的
那不是奇怪的逻辑。这些意思是不同的事情。第一个扩展到f(g(a0, a1, a2, ..., aN))
,第二个扩展为f(g(a0), g(a1), g(a2), ..., g(aN))
。有时您需要前者,有时需要后者。具有允许两者的语法非常重要。
这是您的test3
的样子:
template <typename ... Ts>
void test3_impl(size_t i, Ts&&... args) {
f(std::forward<Ts>(args)...);
}
template <size_t ... Is, typename ... Ts>
void test3(std::index_sequence<Is...>, Ts&&... args) {
int dummy[] = { 0, (test3_impl(Is, args...), void(), 0)... };
}
参数包扩展可以在特定上下文中进行(通常是功能/模板参数/参数列表和Brace-Initializer列表)。像您所做的那样,蓝色的扩展是非法的。为了避免这种情况,我们需要在法律环境中进行此初始化列表。但是,我们必须确保上所述初始化列表并非不明显:
- 它一定不能为空:所以我们在开始时投入
0
- 它必须经过良好的效果:
test3_impl()
返回void,因此我们使用逗号操作员:(<CALL>, void(), 0)
。void()
在这里可以防止逗号操作员过载,添加为详尽,在您的示例中不需要。
最后,该虚拟初始化器列表必须存储在某个地方,因此INT数组是一个好的占位符。
但是,当您写作时:
// Assuming Is... is [I1, IsTail...]
test3_impl(Is..., args...);
这实际上称为f(IsTail..., args...)
,而不是f(args...)
- 1d 智能指针不适用于语法 (*)++
- 助记符和指向成员语法的指针
- 有人能分解一下这个c++模板的语法吗
- C++避免重复声明的语法是什么
- QMetaObject invokeMethod的基于函数指针的语法
- 有充分的理由在h文件中使用include保护而不是cpp文件吗
- 这个语法std::class<>{}(arg1, arg2) 在C++中是什么意思?
- 为什么包含windows.h会产生语法错误,从而阻止类的实例化?(C2146,C2065)
- 单独定义模板化嵌套类方法的正确语法
- 共享指针和具有自定义删除程序的唯一指针之间的语法差异背后的任何原因
- 错误 C2760:语法错误:映射迭代器上意外的标记"标识符",预期的";"
- 为什么我会收到错误 C2143 语法错误:缺少"*"之前的';'?
- 奇怪的代码抛出编译错误模板< J,int aSize=10> C2143:语法错误:在"<"之前缺少";"
- 使用基类指针调用基类的值构造函数的语法是什么?
- 很好的语法来获取对向量/数组数据的大小引用?
- C++语法运算符功能?
- C++使用 rand 定义函数语法
- 什么文件可以修改 atom 的C++语法?
- 创建模板嵌套类实例的语法?
- 模板参数包扩展语法的理由