在C ++中,初始化类
时MyClass(myBigObject s):
s_(s)
{
...
}
看起来好像s
在功能输入时复制一次("按值传递#34;),一次被分配到s_
。
编译器是否足够智能去除第一个副本?
答案 0 :(得分:3)
允许编译器删除第一个副本iff
myBigObject
ctor的用户时,编译器必须知道MyClass
的构造函数或析构函数都没有任何可观察的副作用。
1.9程序执行
本国际标准中的语义描述定义了参数化的非确定性摘要 机。本国际标准对符合实施的结构没有要求。 特别是,它们不需要复制或模拟抽象机器的结构。相反,符合 如下所述,实现需要模拟(仅)抽象机器的可观察行为 下方。
当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数具有副作用。在这种情况下,实现将省略的复制/移动操作的源和目标视为仅仅两种不同的引用同一对象的方式,并且该对象的销毁发生在两个对象的后期时间。在没有优化的情况下销毁.123在下列情况下允许复制/移动操作的省略,称为复制省略(可以合并以消除多份副本):
- 在具有类返回类型的函数的return语句中,当表达式是a的名称时
具有与函数返回类型相同的cvunqualified类型的非易失性自动对象(函数或catch子句参数除外),通过将自动对象直接构造为函数的返回值,可以省略复制/移动操作
- 在throw-expression中,当操作数是非易失性自动对象的名称(函数或catch子句参数除外),其范围不会超出最内层的末尾
封闭try-block(如果有的话),从操作数到异常的复制/移动操作
通过将自动对象直接构造到异常对象
中,可以省略对象(15.1)
- 当复制/移动未绑定到引用(12.2)的临时类对象时
对于具有相同cv-unqualified类型的类对象,可以省略复制/移动操作
将临时对象直接构造到省略的复制/移动的目标中
- 当异常处理程序的异常声明(第15条)声明一个相同类型的对象(cv-qualification除外)作为异常对象(15.1)时,可以通过处理异常声明来省略复制/移动操作作为异常对象的别名,如果程序的含义将保持不变,除了执行异常声明声明的对象的构造函数和析构函数。
&&
更容易获取参数,它允许在任何情况下用移动替换第二个副本。
答案 1 :(得分:1)
编译器无法优化从参数到calss字段的复制。显然,您的对象可以具有复杂的复制构造函数,其副作用无法忽略。
但在您的情况下,您可能希望将复制替换为moving。
您应该编写移动构造函数并使用std::move
将s
移至_s
:
myBigObject(myBigObject&&other);
MyClass(myBigObject s):
s_(std::move(s))
{
...
}
在这种情况下代码如
MyClass obj((myBigObject()));
将以零拷贝结束,因为对象将首先移动到构造函数,然后移动到类字段。
答案 2 :(得分:1)
s
如果传递临时对象,优化编译器可能会省略第一个副本:
MyClass x{ myBigObject() };
这可能只调用一次复制构造函数,因为临时myBigObject将直接构造到构造函数参数s
中。
请注意,这可能会改变程序的可观察行为。
#include <iostream>
struct myBigObject
{
size_t x;
myBigObject() : x() {}
myBigObject(myBigObject && other)
{
std::cout << "Move myBigObject" << std::endl;
}
myBigObject(const myBigObject &other)
{
std::cout << "Copy myBigObject" << std::endl;
x = 12;
}
};
struct MyClass
{
MyClass(myBigObject s)
: s_(s)
{
std::cout << "x of s : " << s.x << std::endl;
std::cout << "x of s_ : " << s_.x << std::endl;
}
myBigObject s_;
};
int main()
{
std::cout << "A:" << std::endl;
MyClass x{ myBigObject() };
std::cout << "B:" << std::endl;
myBigObject y;
MyClass z{ y };
}
打印(https://ideone.com/hMEv1W + MSVS2013,Toolsetv120)
A:
Copy myBigObject
x of s : 0
x of s_ : 12
B:
Copy myBigObject
Copy myBigObject
x of s : 12
x of s_ : 12
s_
由于s_
和s
需要是不同的对象,因此无法省略s_
的副本。
如果您只想在课程中使用myBigObject
的一个副本,则可以选择:
MyClass(myBigObject const & s)
: s_(s)
{
}
MyClass(myBigObject && s)
: s_(std::forward<myBigObject>(s))
{
}
这样,在临时对象和非临时对象只有一个副本的情况下,您看不到任何副本。
更改的代码将打印:
A:
Move myBigObject
B:
Copy myBigObject