2013-03-28 33 views
23

考慮以下內容:移動語義&參數評價順序

std::string make_what_string(const std::string &id); 

struct basic_foo 
{ 
    basic_foo(std::string message, std::string id); 
}; 

struct foo 
    : public basic_foo 
{ 
    foo::foo(std::string id) 
     : basic_foo(make_what_string(id), std::move(id)) // Is this valid? 
    { 
    } 
}; 

因爲在C++參數評價順序是不確定的,我想知道如果 線

basic_foo(make_what_string(id), std::move(id)) 
在上面的代碼

是有效的。

我知道std::move不過是一個強制轉換,但是當std :: string move ctor執行了嗎?在所有參數都被評估之後,是時候調用 的基礎構造函數了?或者是在評估參數期間完成的?在其他 話:

該編譯器做到這一點:

std::string &&tmp2 = std::move(id); 
std::string tmp1 = make_what_string(id); 
basic_foo(tmp1, tmp2); 

這是有效的。或者這個:

std::string tmp2 = std::move(id); 
std::string tmp1 = make_what_string(id); 
basic_foo(tmp1, tmp2); 

這是無效的。請注意,在這兩種情況下,訂單都是「意外」 之一。

+0

其實,代碼是有效的。然而,我相信你的意思是在'base_foo'的構造函數中通過rvalue引用而不是值(不是嗎?)來獲取字符串'id'。 – 2013-03-28 11:51:55

+1

@CassioNeri,沒有代碼是預期:) – Tom 2013-03-28 12:03:36

+0

是的,我意識到,在午餐時間。 :-)非常好的問題。 – 2013-03-28 13:24:19

回答

16

參見1.9節:

除非另有說明,評估個別操作符的操作數和個別表達式的個別表達式是不確定的。

當調用一個函數(函數是否是內聯)中,用任何參數表達相關的每一個值的計算和副作用,或與所述後綴表達式指定被調用的函數,是在執行被調用函數的主體中的每個表達式或語句之前進行排序。 [注意:與不同參數表達式相關聯的值計算和副作用是不確定的。 - 注完]

我認爲問題是,它不是很清楚的參數初始化是否被認爲與參數表達式相關的副作用。但是,它似乎由第5.2.2節備份:

每個參數的初始化和銷燬​​發生在 調用函數的上下文中。

而且也有同款的說明,使得它更清晰一點:

當一個函數被調用時,每個參數(8.3.5)應被初始化(8.5,12.8,12.1 )及其相應的論點。

所以,是的,自變量的初始化是不定相對於彼此排序 - :注完這樣的初始化是不定相對於彼此(1.9)進行測序。在初始化可能發生在任何這些命令:

std::string message = make_what_string(id); 
std::string id = std::move(id); 

std::string id = std::move(id); 
std::string message = make_what_string(id); 

在第二種情況下,make_what_string最終參與工作的移動,從字符串。

因此,即使std::move實際上並沒有移動任何東西,但重要的是,實際的移動對於另一個參數也是不確定的。

basic_string(basic_string&& str)狀態的移動構造函數的定義:

[...] str留在有效狀態與一個未確定的值。

因此,您沒有未定義的行爲,您有未指定的行爲。

+0

@David:是的,因爲從字符串移動可能不再有效,具體取決於實現 – Tom 2013-03-28 11:48:26

+0

@Tom實際上,它沒有指定,沒有未定義。 :) – 2013-03-28 11:50:36

+0

我認爲規範性的論點可以是:或者參數在函數調用之前或之後初始化。在函數調用不可能之後。因此它必須發生在之前。並且參數初始化被規範地定義爲在調用者的上下文中發生(因此它不被評估爲「在被調用函數的主體中」)。所以沒有關於參數評估的參數初始化的排序(也沒有傳遞排序)。 – 2013-03-28 11:58:53

7

這不是真的有效。函數參數評估的順序是未指定的。換句話說,你不知道編譯器是否會選擇這個序列:

tmp1 = make_what_string(id); 
tmp2 = std::move(id); 
basic_foo(tmp1, tmp2); 

或者這一個:

tmp1 = std::move(id); 
tmp2 = make_what_string(id); //id has already been moved from! 
basic_foo(tmp2, tmp1); 
+0

你確定嗎?我已經更新了我的問題,以更好地說明我在問什麼。 – Tom 2013-03-28 11:42:24

+0

但是'std :: move'本身並沒有實際移動任何東西,它只轉換爲右值引用。 – interjay 2013-03-28 11:45:55

+0

@interjay:確實;但是(正如問題所描述的那樣)從std :: move' *的結果形成函數參數的值將會移動它。 – 2013-03-28 11:49:57