我在另一个问题中发布了这段代码,但我对它有了新的怀疑:
#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移动构造函数调用X默认构造函数调用X移动构造函数
而g++的一个是
X默认构造函数称为X默认构造函数
http://liveworkspace.org/code/220erd2美元
任务在哪里??我认为第一个main()行将调用一个移动赋值,而第二个main()行将调用一个复制赋值。
发布于 2013-04-02 18:21:43
// 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;它是用5初始化的x。初始化,虽然看起来很相似,但与赋值不一样。实际上,代码中根本没有赋值,因此将不使用移动或复制赋值运算符。
我们可以看看您给出的每个编译器实际上在做什么:
myNewObject是用make_x创建的。这个打印出了X default constructor called。然后,return myNewObject;将首先将返回值的副本视为一个移动,发现存在一个move构造函数,并调用它。
如果符合或将满足复制操作的省略条件,除非源对象是函数参数,并且要复制的对象由lvalue指定,则首先执行重载解析,以选择副本的构造函数,就好像对象是由rvalue指定的一样。然后将返回值复制到x1中。但是,由于没有看到X copy constructor called输出,这个副本显然被省略了:
当没有绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv非限定类型的类对象时,可以通过将临时对象直接构造到省略复制/移动的目标中来省略复制/移动操作。
其次,在对myNewObject的第二次调用中创建了另一个make_x。这再次打印出X default constructor called。然后,在执行return myNewObject时也会发生同样的情况。返回值的x2构造不会输出任何内容,因为它的构造函数接受X不会执行任何输出。
myNewObject是在make_x中创建的,与MSVC一样。这个打印出了X default constructor called。
现在GCC做了一个MSVC没有做的额外的优化。它意识到,它可能不会麻烦地从myNewObject移到返回值,而只是直接在返回值的位置构造它:
在具有类返回类型的函数中的返回语句中,当表达式是与函数返回类型相同的非易失性自动对象(函数或catch-子句参数除外)的名称时,可以通过将自动对象直接构造到函数的返回值中来省略复制/移动操作。然后,从临时对象构造x1所引起的省略将与MSVC中的情况相同。
对make_x的第二次调用与第一次调用完全相同,但现在x2是由接受X的转换构造函数构造的。当然,这没有输出任何东西。
发布于 2013-04-02 18:20:22
你看到了命名返回值优化,NRVO的效果。以下代码
X f()
{
X tmp;
// ...
return tmp;
}可以完全移除临时,并在函数调用方的返回槽中构造它。在您的示例中,效果就像函数是内联的,直接构造x1和x2。
如果你想看到任务,你可以写:
X x1;
x1 = make_x();https://stackoverflow.com/questions/15771356
复制相似问题