为什么参数包扩展与不同的C ++编译器有不同的工作原理?

参数包扩展由VS2015编译器反转。

我有以下代码:

#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; } 

当我在XCode(clang-700.1.81)中运行这个代码时,我得到了这个结果:

 1 2 3 4 

但是在VS2015中运行相同的代码产生这个输出:

 4 3 2 1 

为什么参数包的扩展取决于编译器的不同? 有没有办法解决它,而不检查平台和编译器版本? 标准没有保证扩展命令的任何内容吗?

这不是参数包扩展顺序不同,它是函数参数评估的顺序。

 f_Swallow ( [&]() { result.push_back(arg); return true; } ()... ) ; 

为了简洁起见,只需给这个lambda命名funcN ,其中N是参数号。 给定四个参数,参数包将被任何符合的编译器扩展为:

 f_Swallow(func1(), func2(), func3, func4()) ; 

函数参数的求值顺序在C ++中是未指定的。 编译器可以按顺序(比如你的Clang版本),按照相反的顺序(比如你的MSVC版本)或者任何它喜欢的顺序来评估它们。 您不能依靠评估订单。

为了得到你想要的,你可以把expression式放到一个上下文中,在这个上下文中指定评估顺序。 例如:

 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; } 

在C ++ 17中,您可以使用折叠expression式执行以下操作:

 template <typename... T> std::vector<int> f(T ...arg) { std::vector<int> result; (result.push_back(arg), ...); return result; } 

我想这也可以写成这样:

 template <typename... T> std::vector<int> f(T ...arg) { std::vector<int> result{ arg... }; return result; } 

不需要创build虚拟std :: initializer_list