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

sun*_*nny 2 c++ templates variadic-templates c++11 list-initialization

我遇到了这个 post可变参数模板函数来连接 std::vector 容器,建议使用以下语法:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

int unpack[] { (append_to_vector(v1, vr), 0)... };
(void(unpack));
Run Code Online (Sandbox Code Playgroud)

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

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

Pra*_*ian 5

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

这将创建一个int包含与参数包大小一样多的元素的s数组vr。数组中的每个元素都是1,这是逗号运算符在评估两个参数后返回的内容。最后一个省略号表示正在完成参数包的包扩展vr

因此,如果您将函数调用为concat_version3(v1, v2, v3)所有参数均为vectors 的位置,则上述表达式将导致

int unpack[]{ (append_to_vector(v1, v2), 1), (append_to_vector(v1, v3), 1) };
Run Code Online (Sandbox Code Playgroud)

在花括号初始化列表中评估表达式的好处是评估的顺序是固定的并且从左到右发生。

§8.5.4/4 [dcl.init.list]

内的初始列表一个的支撑-INIT列表中,初始化子句,包括来自包扩展(14.5.3)的任何结果,在它们出现的顺序进行评估

所以你可以保证v2被附加到v1before v3,这就是你想要的。


(void(unpack));
Run Code Online (Sandbox Code Playgroud)

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


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

int unpack[] { 1, (append_to_vector(v1, vr), 1)... };
//             ^^
Run Code Online (Sandbox Code Playgroud)

在原始版本中,如果您调用函数 as concat_version3(v1),即使用空参数包,代码将无法编译,因为您将尝试创建一个零大小的数组,添加额外的元素可以解决该问题。

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

int unpack[] { 1, (append_to_vector(v1, vr), void(), 1)... };
Run Code Online (Sandbox Code Playgroud)

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


最后,如果你有一个能理解折叠表达式的编译器,你就可以去掉整个数组的技巧,简单地写

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;
}
Run Code Online (Sandbox Code Playgroud)

现场演示

注意:void由于clang 错误,需要在转换后的额外括号。