Parameter pack expansion is reversed by the VS2015 compiler.
I have the following code:
#include <iostream>
#include <vector>
template <typename... T>
void f_Swallow(T &&...)
{
}
template <typename... T>
std::vector<int> f(T ...arg)
{
std::vector<int> result;
f_Swallow
(
[&]()
{
result.push_back(arg);
return true;
}
()...
) ;
return result;
}
using namespace std;
int main()
{
auto vec = f(1,2,3,4);
for (size_t i = 0; i < vec.size(); ++i)
cout << vec[i] << endl;
}
When I run this code in XCode (clang-700.1.81), I get this result:
1
2
3
4
But the same code run in VS2015 produces this output:
4
3
2
1
Why are the parameter packs expanded differently depending on the compiler? Is there a way to fix it without checking the platform and compiler version? Doesn't the standard guarantee anything about expansion order?
It's not the order of parameter pack expansion which is different, it's the order of function argument evaluation.
f_Swallow
(
[&]()
{
result.push_back(arg);
return true;
}
()...
) ;
For sake of brevity, lets just give that lambda the name funcN
where N
is the parameter number. Given four arguments, the parameter pack will be expanded by any conforming compiler into this:
f_Swallow(func1(), func2(), func3, func4()) ;
The order of evaluation of function arguments is unspecified in C++. The compiler could evaluate them in-order (like your version of Clang), in reverse order (like your version of MSVC), or in any order it likes. You cannot count on the evaluation order.
To get what you want, you could put the expressions into a context in which the order of evaluation is specified. For example:
template <typename... T>
std::vector<int> f(T ...arg)
{
std::vector<int> result;
(void)std::initializer_list<int> { (result.push_back(arg), 0)... };
return result;
}
In C++17, you'll be able to do the following with fold expressions:
template <typename... T>
std::vector<int> f(T ...arg)
{
std::vector<int> result;
(result.push_back(arg), ...);
return result;
}
I figured that it could be also written just like this:
template <typename... T>
std::vector<int> f(T ...arg)
{
std::vector<int> result{ arg... };
return result;
}
No need to create dummy std::initializer_list
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With