int unpack[] { (append_to_vector(v1, vr), 1)... };
// ^^ | | ||| | array of ints
// ^ | | ^ array initializer
// ^ | comma operator
// ^^^ pack expansion
This is creating an array of int
s containing as many elements as the size of the parameter pack vr
. Each element in the array is 1
, which is what the comma operator returns after evaluating both arguments. The final ellipsis indicates pack expansion of the parameter pack vr
is being done.
So if you were to call your function as concat_version3(v1, v2, v3)
where all arguments are vector
s, then the above expression would result in
int unpack[]{ (append_to_vector(v1, v2), 1), (append_to_vector(v1, v3), 1) };
The nice thing about evaluating expressions within a braced-init-list is that the order of evaluation is fixed and happens left to right.
§8.5.4/4 [dcl.init.list]
Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (14.5.3), are evaluated in the order in which they appear.
So you're guaranteed that v2
gets appended to v1
before v3
, which is what you want.
(void(unpack));
This is just a way to avoid unused variable warnings from the compiler.
Now, I would write your unpack
initialization a bit differently.
int unpack[] { 1, (append_to_vector(v1, vr), 1)... };
// ^^
In the original, if you called the function as concat_version3(v1)
, i.e. with an empty parameter pack, the code wouldn't compile because you'd be attempting to create a zero sized array, adding the extra element fixes that problem.
Furthermore, if you were using the above expression in more generic code where you didn't know what the return type of append_to_vector
was, then you'd also need to guard against the possibility of it returning a type that overloads the comma operator. In that case you'd write
int unpack[] { 1, (append_to_vector(v1, vr), void(), 1)... };
By adding the void()
expression in between you ensure that no overloaded comma operator is selected, and the built-in one is always called.
Finally, if you have a compiler that understands fold expressions, you can do away with the whole array trick and simply write
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;
}
Live demo
Note: the extra parentheses after the void
cast are required due to a clang bug.
append_to_vector(v1, X)
once for each of the variadic arguments given, withX
replaced by each element in turn. I call it the "dummy array comma operator hack", don't know if it has an official name :) – Attila