为什么参数包扩展与不同的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