The accepted answer of the Q&A Variadic template pack expansion makes use of a common pre-C++17 (prior to fold expressions) approach to "folding" of an unexpanded template parameter pack.
I've seen a few different variations of this technique; taking the Q&A above as an example:
#include <initializer_list>
#include <iostream>
#include <utility>
template <typename T> static void bar(T) {}
template <typename... Args> static void foo1(Args &&... args) {
using expander = int[];
// Left-most void to avoid `expression result unused [-Wunused-value]`
(void)expander{0, ((void)bar(std::forward<Args>(args)), 0)...};
}
template <typename... Args> static void foo2(Args &&... args) {
int dummy[] = {0, ((void)bar(std::forward<Args>(args)), 0)...};
// To avoid `unused variable 'dummy' [-Wunused-variable]`
(void)dummy;
}
template <typename... Args> static void foo3(Args &&... args) {
// Left-most void to avoid `expression result unused [-Wunused-value]`
(void)std::initializer_list<int>{((void)bar(std::forward<Args>(args)), 0)...};
}
template <typename... Args> static void foo4(Args &&... args) {
auto l = {0, ((void)bar(std::forward<Args>(args)), 0)...};
// To avoid `unused variable 'l' [-Wunused-variable]`
(void)l;
}
int main() {
foo1(1, 2, 3, "3");
foo1();
foo2(1, 2, 3, "3");
foo2();
foo3(1, 2, 3, "3");
foo3();
foo4(1, 2, 3, "3");
foo4();
return 0;
}
Are any of these variations (or other variations) considered "the idiomatic one"? Are there any subtleties/differences between them that one would need to take care with?
The std::initializer_list
approach does not require the somewhat elusive left-most 0
in the braced-init-list, as an initializer list may be empty, whereas an array may not be zero(/negative)-sized. Possibly this could be an argument for foo3
(arguably slightly less complexity at the cost of an additional #include
).
Are any of these variations (or other variations) considered "the idiomatic one"?
I would say yes.
Are there any subtleties/differences between them that one would need to take care with?
There are mostly equivalent, but
foo3
and foo4
require #include <initializer_list>
whereas foo1
and foo2
don't.