2013-04-15 37 views
11

括在初始化列表一个可变参数模板的参数应该保证他们为了评估,但没有发生在这里:C++ 11初始化列表:为什么不是这方面的工作

#include <iostream> 
using namespace std; 


template<class T> void some_function(T var) 
{ 
    cout << var << endl; 
} 

struct expand_aux { 
    template<typename... Args> expand_aux(Args&&...) { } 
}; 

template<typename... Args> inline void expand(Args&&... args) 
{ 
    bool b[] = {(some_function(std::forward<Args>(args)),true)...}; // This output is 42, "true", false and is correct 
    cout << "other output" << endl; 
    expand_aux temp3 { (some_function(std::forward<Args>(args)),true)... }; // This output isn't correct, it is false, "true", 42 
} 

int main() 
{ 
    expand(42, "true", false); 

    return 0; 
} 

怎么回事?

+2

编译器错误?从您提供的链接中,从gcc切换到叮当或英特尔将产生您期望的结果。 –

+1

你是对的德鲁,铛是这样做的 – Paul

+0

(或未定义的行为...) –

回答

12

这似乎是一个错误。输出应该是你期望的。

尽管对于构造函数调用的参数的评估顺序无法保证,但一般对于支撑初始化程序列表中的表达式求值顺序有保证。

每个段落的C++ 11标准的8.5.4/4:

在一个支撑-INIT-列表的初始化列表,初始化器子句,包括任何从包产生 扩展(14.5.3),按它们出现的顺序进行评估。即,与给定的初始值设定子句相关联的每个值的计算和 副作用的每一个值的计算之前进行测序和侧它后面在初始化列表的逗号分隔的列表中的任何初始化子句相关 效果。 [注意:不管初始化的语义如何,这个评估顺序都成立;例如,适用 当初始化列表中的元素被解释为构造函数调用的参数,即使 通常有在通话的论点没有排序限制。末端注]

+3

似乎gcc只是应用它的正常的函数调用参数评估顺序来统一初始化调用是正常的构造函数调用。但是tbh,即使调用了相同的构造函数,'Foo x {a,b};和'Foo x(a,b);'也不相同。 –

+0

@ArneMertz:确实,统一的初始化*方式*比它假装的更不统一。 –

+0

我不会那么说。不管是否必须用C++ 03中的不同大括号/括号来书写,将聚合初始化程序列表从左向右进行评估,而非反向聚合显然不是很统一。 Imo它是一个(现在不可修正的)bug或至少是标准中的不一致,它允许编译器评估函数,尤其是构造函数参数,像gcc一样从右向左评估。定义eval订单是确保不同编译器之间可移植性的正确选择,并且在过去可以用于函数参数评估。 –

4

如上所述,你的问题是一个编译器错误。按照书面形式,您的代码应按顺序评估其参数。

我的建议是要明确你想做什么,以及你在做什么,避免滥用逗号操作符(作为一个例外,如果some_function返回了一个覆盖类型,你的代码可能会表现得很奇怪operator,)或使用初始化程序列表保证(虽然标准,但也相对模糊)。

我对解决方案走的是写,然后使用do_in_order

// do nothing in order means do nothing: 
void do_in_order() {} 
// do the first passed in nullary object, then the rest, in order: 
template<typename F0, typename... Fs> 
void do_in_order(F0&& f0, Fs&&... fs) { 
    std::forward<F0>(f0)(); 
    do_in_order(std::forward<Fs>(fs)...); 
} 

你使用这样的:

do_in_order([&]{ some_function(std::forward<Args>(args)); }...); 

你包你想在一个匿名零元全做动作捕获lambda,然后使用...来创建所述lambda的一整套实例,并传递给do_in_order,通过完美的转发来调用它们。

对于编译器来说,这应该很容易,并且可以简化为一系列调用。它说明了它直接做了什么,并且不需要奇怪的无效转换,使用逗号运算符或值被丢弃的数组。

相关问题