【发布时间】:2017-12-25 13:07:59
【问题描述】:
我正在做一些实验,看看除了复制省略、RVO、NRVO 情况之外何时执行复制。
所以我写了一些这样的代码:
class X {
public:
X() { std::cout << "Default constructor" << std::endl; }
X(const X&) { std::cout << "Copy constructor" << std::endl; }
X(X&&) { std::cout << "Move constructor" << std::endl; }
X& operator=(const X) {
std::cout << "Assignment operator" << std::endl;
return *this;
}
X& operator=(X&&) {
std::cout << "Move assignment operator" << std::endl;
return *this;
}
~X() { std::cout << "Destructor" << std::endl; }
};
class Y {
private:
X x;
public:
const X& getX() const {
std::cout << "getX" << std::endl;
return x;
}
};
int main() {
Y y;
std::cout << "assign to ref" << std::endl;
const X& x1 = y.getX();
(void)x1;
std::cout << "assign to const" << std::endl;
const X x2 = y.getX();
return 0;
}
我收到以下输出:
Default constructor
assign to ref
getX
assign to const
getX
Copy constructor
Destructor
Destructor
使用 gcc 编译或使用 -O3 进行 clang 编译并尝试 -std=c++{11,14,17} 都产生相同的输出。
令我惊讶的是,我没想到在使用 y.getX(); 时会执行任何复制;到一个 const 变量。这是我经常使用的东西,只是为了简化我在以下代码中对该变量及其成员的访问,但我并没有通过 const 引用执行它,而是我只是使用 const 希望编译器将它视为重命名。
有谁知道为什么要执行该副本?我想到的唯一原因是它使代码线程安全。如果有多个线程处理对象 y,那么我对 const 的分配毕竟不会是那个 const。因为它只会引用对象 y 中的成员 x。这可能会被其他线程更改。但我不确定这是否是真正的意图。
【问题讨论】:
-
在 C++ 中,你得到你想要的。如果您定义一个对象并通过复制对其进行初始化,您将得到两个相同的对象,而不是现有对象的某个句柄。
-
好吧,也许您只是忘记了
&?通过这种方式,您要求并获得一份副本,而不是参考资料。const X&将再次按您期望的方式工作。 -
实际上我期待两者的行为相同。正如你所看到的,我对它们都进行了测试,我很确定 const 参考不会复制,但不能确定为什么只是 const 也会复制。因为它应该是对y中的成员变量的只读访问。
-
不,应该是一个新的对象,和原来的一样,正好是
const。如果对象可以根据周围的代码以及优化器的计算结果隐式出现和消失并改变生命周期,那么编程将是疯狂的。 -
@Quentin “如果对象可以根据周围的代码和优化器的计算结果隐式出现和消失并改变生命周期,那么编程将是疯狂的。” 呃,RVO 确实如此正是如此。
标签: c++ copy-constructor copy-elision rvo nrvo