int a [] {(functioncall(a1,a2),0)...}; (无效的(a));这个语法是什么意思?

时间:2015-11-10 00:17:13

标签: c++ templates c++11 variadic-templates list-initialization

我发现这篇文章variadic template function to concatenate std::vector containers建议使用以下语法:

template<typename T>
void append_to_vector(std::vector<T>& v1, const std::vector<T>& v2) {
  std::cout << v2[0] << std::endl;
  for (auto& e : v2) v1.push_back(e);
}


template<typename T, typename... A>
std::vector<T> concat_version3(std::vector<T> v1, const A&... vr) {
    int unpack[] { (append_to_vector(v1, vr), 1)... };
    (void(unpack));
    return v1;
}

我开始玩它以了解它是如何工作的,因为我还没有看到这个:

int unpack[] { (append_to_vector(v1, vr), 0)... };
(void(unpack));

这似乎是某种动态生成的初始化列表,也有副作用?我也对以上0无关紧要的事实感到困惑。我替换了-1和5,这些值中的每一个都运行得很好。

那么有人可以告诉我这个技术/语法的名称以及上面两行中究竟发生了什么?如果我错过了相关的SO帖子,我真的很感激任何指点和道歉。

1 个答案:

答案 0 :(得分:4)

int unpack[] { (append_to_vector(v1, vr), 1)... };
//        ^^ |                          |   ||| |    array of ints
//           ^                          |    |  ^    array initializer
//                                      ^    |       comma operator
//                                          ^^^      pack expansion

这是创建一个int数组,其中包含与参数包vr的大小一样多的元素。数组中的每个元素都是1,这是评估两个参数后comma operator返回的内容。最后的省略号表示参数包vr的{​​{3}}正在完成。

因此,如果你要将你的函数称为concat_version3(v1, v2, v3),其中所有参数都是vector s,那么上面的表达式将导致

int unpack[]{ (append_to_vector(v1, v2), 1), (append_to_vector(v1, v3), 1) };

评估 braced-init-list 中的表达式的好处是评估的顺序是固定的,从左到右发生。

§8.5.4/ 4 [dcl.init.list]

  

braced-init-list initializer-list 中, initializer-clauses ,包括包扩展产生的任何结果( 14.5.3),按照它们出现的顺序进行评估

因此,我们保证在v2之前将v1附加到v3,这就是您想要的。

(void(unpack));

这只是一种避免编译器未使用的变量警告的方法。

现在,我会以不同的方式编写unpack初始化。

int unpack[] { 1, (append_to_vector(v1, vr), 1)... };
//             ^^

在原文中,如果您将函数调用为concat_version3(v1),即使用空参数包,则代码将无法编译,因为您尝试创建零大小的数组,添加额外的元素修复那个问题。

此外,如果您在更通用的代码中使用上面的表达式,而您不知道append_to_vector的返回类型是什么,那么您还需要防止它返回类型的可能性重载逗号运算符。在那种情况下你会写

int unpack[] { 1, (append_to_vector(v1, vr), void(), 1)... };

通过在中间添加void()表达式,确保没有选择重载的逗号运算符,并始终调用内置的运算符。

最后,如果你有一个理解pack expansion的编译器,你可以取消整个数组技巧并简单地写

template<typename T, typename... A>
std::vector<T> concat_version3(std::vector<T> v1, const A&... vr)
{
    (void)(((append_to_vector(v1, vr), void()), ...));
    return v1;
}

fold expressions

注意:由于Live demo而需要void强制转换后的额外括号。