我在另一个问题上发布了此代码,但我对它有了新的疑问:
#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 default 名为X的构造函数移动构造函数,名为
g ++的一个是
X默认构造函数,名为X默认构造函数,名为
http://liveworkspace.org/code/220erd $ 2
作业在哪里?我认为第一个main()行将调用移动赋值,第二个main()行将调用复制赋值
答案 0 :(得分:8)
// 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;
这不是分配给x
的5;使用x
初始化5
。初始化虽然看起来很相似,但与赋值不同。实际上,代码中根本不会发生任何赋值,因此不会使用移动或复制赋值运算符。
我们可以看看你给出的每个编译器实际上在做什么:
MSVC
首先,在myNewObject
中创建make_x
。这打印出X default constructor called
。然后return myNewObject;
将复制视为返回值作为移动,发现有一个移动构造函数,并调用它。
当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定,重载决策以选择构造函数首先执行复制,就像对象是由右值指定一样。
然后将返回值复制到x1
。但是,这个副本显然已被删除,因为我们看不到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子句参数除外)的名称时,通过将自动对象直接构造到函数的返回值
中,可以省略复制/移动操作
然后,在MSVC中执行从临时对象构造x1
所导致的相同省略。
对make_x
的第二次调用与第一次调用的方式完全相同,除了现在x2
由转换构造函数构造X
。当然,这不会输出任何内容。
答案 1 :(得分:5)
你正在看到named return value optimization,NRVO的效果。以下代码
X f()
{
X tmp;
// ...
return tmp;
}
可以完全删除临时值并在函数调用者的返回槽中构造它。在您的示例中,效果就像函数内联并直接构造x1
和x2
。
如果您希望看到作业,可以写下:
X x1;
x1 = make_x();