複製省略的目的

標準中有一些位置可以複製或移動物件以初始化物件。複製省略(有時稱為返回值優化)是一種優化,在某些特定情況下,即使標準表明它必須發生,也允許編譯器避免複製或移動。

考慮以下功能:

std::string get_string()
{
  return std::string("I am a string.");
}

根據標準的嚴格措辭,此函式將初始化臨時 std::string,然後將其複製/移動到返回值物件中,然後銷燬臨時值。標準非常明確,這就是程式碼的解釋方式。

複製省略是允許 C++編譯器忽略臨時及其後續複製/銷燬的建立的規則。也就是說,編譯器可以為臨時表達初始化表示式,並直接從中初始化函式的返回值。這顯然可以節省效能。

但是,它對使用者有兩個可見的影響:

  1. 該型別必須具有已被呼叫的複製/移動建構函式。即使編譯器省略了複製/移動,該型別仍必須能夠被複制/移動。

  2. 在可能發生 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。並且無論編譯器是否省略特定情況,該型別仍必須支援被省略的操作。因此,如果省略了複製構造,則型別必須仍具有複製建構函式,即使它不會被呼叫。