我在另一个问题上发布了这个代码,但我对此有了新的疑问:
#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
}
Run Code Online (Sandbox Code Playgroud)
在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;
}
Run Code Online (Sandbox Code Playgroud)
首先,这些不是复制构造函数和复制赋值运算符,X2因为它们接受类型的参数X.第一种实际上称为转换构造函数,因为它可以从一个转换X为一个X2.
int x = 5;
Run Code Online (Sandbox Code Playgroud)
这不是5分配给x; 它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
首先,与MSVC一样,myNewObject创建make_x.打印出来X default constructor called.
现在,GCC进行了MSVC没有做的额外优化.它意识到它可能不会费心转移myNewObject到返回值,而只是直接在返回值的位置构造它:
在具有类返回类型的函数的return语句中,当表达式是具有与函数返回类型相同的cvunqualified类型的非易失性自动对象(函数或catch子句参数除外)的名称时,副本通过将自动对象直接构造到函数的返回值中,可以省略/ move操作
然后,x1如在MSVC中那样执行从临时对象构造所引起的相同省略.
第二次调用以make_x与第一次调用完全相同的方式发生,除了现在x2由转换构造函数构造,它采用了X.当然,这不会产生任何结果.
您正在查看命名返回值优化 NRVO的效果.以下代码
X f()
{
X tmp;
// ...
return tmp;
}
Run Code Online (Sandbox Code Playgroud)
可以完全删除临时并在函数调用者的返回槽中构造它.在您的示例中,效果就像函数内联x1并x2直接构造.
如果你想看到作业,你可以写:
X x1;
x1 = make_x();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
198 次 |
| 最近记录: |