2016-12-12 32 views
0

的考虑下面的辅助功能理由模板参数包扩展语法

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()函数调用后发生。这也是合理的,因为g()被称为与每个args

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的方式,而是从...之前的子表达式被折叠(或者如果你喜欢的话重复)关于可变参数。在test3的情况下,我们相对于Is“折叠”表达式test3(Is, args...)。然而,我们必须编写test3(Is..., args...)而不是test3(Is, args...)...

有了这个奇怪的标准逻辑,你也可以写f(g(args...))而不是f(g(args)...) - 但是这是无效的。看起来语言在不同的上下文中使用不同的逻辑。

不同语法背后的基本原理是什么?

+2

无法使用参数包扩展,而无需使用[技巧](http://stackoverflow.com/链通话a/17340003/1794345) – Rerito

+0

@Rerito谢谢,但我知道这一招。不幸的是,这并没有回答这个问题。 – plasmacel

回答

7

test3的情况下,我们将表达式test3(Is, args...)相对于Is“折叠”。然而,我们必须编写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))。有时你需要前者,有时你需要后者。具有允许两者的语法非常重要。

0

这是你的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)... };  
} 

参数包扩展可以在特定情境下的地方(通常是函数/模板参数/参数列表和括号,初始化列表)。像你所做的那样,蓝色的扩张是非法的。为了规避这一点,我们需要在法律背景下进行,这里是一个初始化列表。但是,我们必须确保不会形成不良的上述初始化列表:

  • 它不能为空:test3_impl()返回void:所以我们在一开始
  • 它必须得到很好的输入扔在一个0 ,所以我们使用逗号运算符:(<CALL>, void(), 0)void()在这里是为了防止逗号运算符重载,它被添加为详尽的,在您的示例中不是必需的。

最后,那个虚拟初始值列表必须存储在某个地方,所以int数组是一个很好的占位符。

但是,当你写:

// Assuming Is... is [I1, IsTail...] 
test3_impl(Is..., args...); 

这实际上是调用f(IsTail..., args...),而不是f(args...)