複製省略的目的
標準中有一些位置可以複製或移動物件以初始化物件。複製省略(有時稱為返回值優化)是一種優化,在某些特定情況下,即使標準表明它必須發生,也允許編譯器避免複製或移動。
考慮以下功能:
std::string get_string()
{
return std::string("I am a string.");
}
根據標準的嚴格措辭,此函式將初始化臨時 std::string
,然後將其複製/移動到返回值物件中,然後銷燬臨時值。標準非常明確,這就是程式碼的解釋方式。
複製省略是允許 C++編譯器忽略臨時及其後續複製/銷燬的建立的規則。也就是說,編譯器可以為臨時表達初始化表示式,並直接從中初始化函式的返回值。這顯然可以節省效能。
但是,它對使用者有兩個可見的影響:
-
該型別必須具有已被呼叫的複製/移動建構函式。即使編譯器省略了複製/移動,該型別仍必須能夠被複制/移動。
-
在可能發生 elision 的情況下,不保證複製/移動建構函式的副作用。考慮以下:
Version >= C++ 11
struct my_type
{
my_type() = default;
my_type(const my_type &) {std::cout <<"Copying\n";}
my_type(my_type &&) {std::cout <<"Moving\n";}
};
my_type func()
{
return my_type();
}
什麼叫 func
呢?好吧,它永遠不會列印複製,因為臨時是一個右值,而 my_type
是一個可移動的型別。它會列印移動嗎?
如果沒有複製省略規則,則需要始終列印移動。但由於存在複製省略規則,移動建構函式可能會被呼叫,也可能不會被呼叫; 它依賴於實現。
因此,你不能依賴於在可以省略的情況下呼叫複製/移動建構函式。
因為 elision 是一種優化,所以在所有情況下,編譯器可能都不支援 elision。並且無論編譯器是否省略特定情況,該型別仍必須支援被省略的操作。因此,如果省略了複製構造,則型別必須仍具有複製建構函式,即使它不會被呼叫。