2013-04-15 36 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,通過完美的轉發來調用它們。

對於編譯器來說,這應該很容易,並且可以簡化爲一系列調用。它說明了它直接做了什麼,並且不需要奇怪的無效轉換,使用逗號運算符或值被丟棄的數組。

相關問題