【发布时间】:2015-09-03 08:18:07
【问题描述】:
我发现了一个明显忽略 const-ness 的代码的奇怪行为:
#include <iostream>
using std::cerr;
class A
{
public:
A() { cerr << "A::A()\n"; }
A(const A &a) { cerr << "A::A(const A&)\n"; }
A(A &&) { cerr << "A::A(A&&)\n"; }
A & operator = (const A &a) { cerr << "A::operator=(const A&)\n"; return *this; }
A & operator = (A &&a) { cerr << "A::operator(A&&)\n"; return *this; }
~A() { cerr << "A::~A()\n"; }
const A get() const { cerr << "const A A::get() const\n"; return A(); }
A get() { cerr << "A A::get()\n"; return A(); }
};
int main()
{
const A a;
A b = a.get();
}
首先,我在这里所期望的:a 是一个常量,因此调用了 get() 的常量版本。接下来,常量对象被返回,但左侧是非常量对象b,所以应该调用复制构造函数。哪个不是:
A::A()
const A A::get() const
A::A()
A::~A()
A::~A()
这种行为是 c++ 标准所期望的吗? 那么,RVO 可以简单地忽略临时对象的常量性吗?以及如何在此处强制执行复制?
禁用复制省略的输出 (-fno-elide-constructors) 进行了额外的移动和预期的复制构造函数调用:
A::A()
const A A::light_copy() const
A::A()
A::A(A&&)
A::~A()
A::A(const A&)
A::~A()
A::~A()
A::~A()
如果a对象不是常量,那么就是两步不复制,这也是意料之中的。
PS。这种行为对我来说很重要,因为我看到的是打破浅拷贝 const-strictness:对于 get() 的 const 版本(实际上是 shallow_copy()),我需要确保不会修改返回的对象,因为返回的对象是浅拷贝,对浅拷贝的修改会影响“父”对象(可能是常量)。
【问题讨论】:
-
实际返回值优化 (RVO)。
-
“light_copy”是指“浅拷贝”吗?
-
您确定要在
get方法中返回新创建的对象A()而不是*this?如果是这样,这些方法可以是static。但是在这种情况下,编译器只接受其中一个。 -
T.C. 的回答告诉您,您看到的行为是标准规定的。我怀疑你在混淆概念。对浅拷贝的更改将始终“传播”到浅拷贝的“源”。现在听起来你想要一个浅拷贝,直到浅拷贝被改变。那将是写时复制或句柄习语。请注意,写时复制很难正确处理,并且在多线程世界中表现不佳(这就是标准不再允许使用 CoW 实现 std::string 对象的原因)。
标签: c++ c++11 constants copy-constructor