[英]Why in C++11 or C++14 does the compiler implicitly delete the copy constructor when I declare a move assignment operator?
[英]C++11 - I lost a move/copy assignment
我在另一個問題上發布了這個代碼,但我對此有了新的疑問:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class X
{
public:
std::vector<double> data;
// Constructor1
X():
data(100000) // lots of data
{
cout << "X default constructor called";
}
// Constructor2
X(X const& other): // copy constructor
data(other.data) // duplicate all that data
{
cout << "X copy constructor called";
}
// Constructor3
X(X&& other): // move constructor
data(std::move(other.data)) // move the data: no copies
{
cout << "X move constructor called";
}
X& operator=(X const& other) // copy-assignment
{
cout << "X copy assignment called";
data=other.data; // copy all the data
return *this;
}
X& operator=(X && other) // move-assignment
{
cout << "X move assignment called";
data=std::move(other.data); // move the data: no copies
return *this;
}
};
class X2
{
public:
std::vector<double> data;
// Constructor1
X2():
data(100000) // lots of data
{}
// Constructor2
X2(X const& other): // copy constructor
data(other.data) // duplicate all that data
{}
X2& operator=(X const& other) // copy-assignment
{
data=other.data; // copy all the data
return *this;
}
};
X make_x()
{
X myNewObject; // Il normale costruttore viene chiamato qui
myNewObject.data.push_back(22);
return myNewObject; // Si crea un oggetto temporaneo prima di ritornare con il move constructor perchè myNewObject dev'essere distrutto
}
int main()
{
X x1 = make_x(); // x1 has a move constructor
X2 x2 = make_x(); // x2 hasn't a move constructor
}
在main()行中,我希望調用移動賦值和復制賦值......但它們不會!
MSVC2012輸出是:
X默認構造函數名為X move constructor,名為X默認構造函數,名為X move constructor,名為
而g ++就是
X默認構造函數調用X默認構造函數調用
http://liveworkspace.org/code/220erd $ 2
作業在哪里? 我認為第一個main()行將調用移動賦值,第二個main()行將調用復制賦值
// Constructor2
X2(X const& other): // copy constructor
data(other.data) // duplicate all that data
{}
X2& operator=(X const& other) // copy-assignment
{
data=other.data; // copy all the data
return *this;
}
首先,這些不是X2
的復制構造函數和復制賦值運算符,因為它們接受X
類型的參數。 第一種實際上稱為轉換構造函數,因為它可以從X
轉換為X2
。
int x = 5;
這不是5分配給x
; 它是x
與正被初始化5
。 初始化雖然看起來很相似,但與賦值不同。 實際上,代碼中根本不會發生任何賦值,因此不會使用移動或復制賦值運算符。
我們可以看看你給出的每個編譯器實際上在做什么:
MSVC
首先, myNewObject
在創建make_x
。 這打印出X default constructor called
。 然后return myNewObject;
將復制視為返回值作為移動優先,發現有一個移動構造函數,並調用它。
當滿足或將滿足復制操作的省略標准時,除了源對象是函數參數這一事實,並且要復制的對象由左值指定,重載決策選擇復制的構造函數是首先執行,好像對象是由右值指定的。
然后將返回值復制到x1
。 但是,這個副本顯然已被刪除,因為我們看不到X copy constructor called
output的X copy constructor called
:
當一個未綁定到引用(12.2)的臨時類對象被復制/移動到具有相同cv-nonqualified類型的類對象時,可以通過將臨時對象直接構造到目標中來省略復制/移動操作省略的復制/移動
其次,另一個myNewObject
在第二次調用創建make_x
。 這再次打印出X default constructor called
。 然后在return myNewObject
時發生相同的移動。 從返回值構造x2
不會輸出任何內容,因為其帶有X
構造函數不會執行任何輸出。
GCC
首先, myNewObject
在創建make_x
,就像MSVC。 這打印出X default constructor called
。
現在,GCC進行了MSVC沒有做的額外優化。 它意識到它可能不會費心從myNewObject
移動到返回值,而只是直接在返回值的位置構造它:
在具有類返回類型的函數的return語句中,當表達式是具有與函數返回類型相同的cvunqualified類型的非易失性自動對象(函數或catch子句參數除外)的名稱時,副本通過將自動對象直接構造到函數的返回值中,可以省略/ move操作
然后,如在MSVC中那樣執行從臨時對象構造x1
導致的相同省略。
對make_x
的第二次調用以與第一次調用完全相同的方式發生,除了現在x2
由采用X
的轉換構造函數構造。 當然,這不會產生任何結果。
您正在查看命名返回值優化 NRVO的效果。 以下代碼
X f()
{
X tmp;
// ...
return tmp;
}
可以完全刪除臨時並在函數調用者的返回槽中構造它。 在您的示例中,效果就像函數內聯並直接構造x1
和x2
。
如果你想看到作業,你可以寫:
X x1;
x1 = make_x();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.