2014-02-20 22 views
2

考慮一個功能,通過價值需要一個對象,執行它的一些操作,並返回該對象,例如:如果返回參數,可以複製elision嗎?

std::string MyToUpper (std::string s) 
{ 
     std::transform(s.begin(), s.end(), s.begin(), std::toupper); 
     return s; 
} 

現在你罵一個臨時此功能:

std::string Myupperstring = MyToUpper("text"); 

概念有不需要複製。現代編譯器能否在這種情況下刪除所有副本?如果沒有,是否只有動作?這種情況怎麼樣:

std::string Mylowerstring("text"); 
std::string Myupperstring = MyToUpper(std::move(Mylowerstring)); 
+0

在這種情況下,編譯器不允許在返回中刪除副本,並且AFAIK在這裏也沒有隱式的'移動'。 – Simple

+2

@Simple:如果類型是可移動的,返回值將被移動(如'字符串在這裏)。 –

+0

與您的問題無關,但您的代碼具有未定義的行爲(假設它編譯了,可能不會)。 'std :: transform'會用'char'調用'std :: toupper',如果簡單的'char'被簽名(通常是這種情況),會導致未定義的行爲。 –

回答

4

兩個概念副本中至多有一個可以省略。如果您通過臨時函數,那麼該複製可以被刪除,每個C++ 11 12.8/31的第三個項目符號:

當臨時類對象...將被複制/移動..複製/移動操作可以省略

返回不能被忽略;只能給臨時來完成(每個上面引述的規則)或局部變量,每個第一彈:

return語句中

...當表達式是一個 非易失性自動對象的名稱(不是函數或catch子句參數其他)...複製/移動操作可以省略

在沒有省音的,返回值被視爲右值,如果可能移​​動(和這是可能的);函數參數如果它們是右值,則移動它們,因爲它們都在您的示例中。

2

我不這麼認爲。其中一些副本可以並且可能會被刪除,但NVRO無法應用,因爲它計算的 變量與返回值 構建在相同的位置。除了值參數外,參數是由調用者構造的 ,他們無法看到(一般情況下)將返回參數 ,因此無法在正確的位置構建它。

1

概念上沒有需要的副本。現代編譯器能否在這種情況下刪除所有副本?

是的,如果函數是內聯的,則是可能的。但是,我想考慮下面的代碼示例而不是您自己的代碼示例,因爲std::string是一個充滿晦澀優化的野獸。

所以我們來考慮一個int的例子。來電者有{1, 2, 3},並且想從中創建一個std::vector<int>,其中包含{2, 4, 6}。 (這大致上是在呼叫者處具有文字"text"並且想要構建包含"TEXT"std::string的模擬。)

代碼:

#include <cstdio> 
#include <vector> 
using namespace std; 

vector<int> mult(vector<int> v) { 
    for (int& e : v) 
    e *= 2; 
    return v; 
} 

int main() { 
    vector<int> v(mult({1, 2, 3})); 
    for (int i : v) 
    printf("%d\n", i); 
} 

如果我用gcc 4.7.2作爲編譯:g++ -O3 -fwhole-program -Wall -Wextra -std=c++11 -S file.cpp,我得到了大會只有一個std::vector<int>析構函數調用。 該矢量是在原地創建的。生成的程序集儘可能好。

如果我編譯完全相同的代碼,但省略了-fwhole-program旗,mult()功能沒有得到內聯,我得到兩次調用的std::vector<int>析構函數。生成的組件也比前一種情況差得多。

鏘不知道-fwhole-program標誌,所以我增加了static關鍵字mult()

static vector<int> mult(vector<int> v) { ...

,然後它也創造就地載體。


From James Kanze's answer:

與值參數,該參數是被調用者,誰 看不到(一般),該參數將返回構造,所以 不能建立在正確的地點。

我做了什麼上面(內聯mult())使我們能夠調用方可以看到該參數將返回和效果,結果就地構建。