我正在做一些实验,看看除了复制省略、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 编译并尝试 -std=c++{11,14,17} 都产生相同的输出。
令我惊讶的是,我没想到使用 y.getX(); 时会执行任何复制;到一个 const 变量。这是我经常使用的东西,只是为了简化我在以下代码中对该变量及其成员的访问,但我并没有通过 const 引用执行它,而是我只是使用 const 希望编译器将它视为重命名。
有谁知道为什么要执行该副本?我想到的唯一原因是它使代码线程安全。如果有多个线程处理对象 y,那么我对 const 的分配毕竟不会是那个 const。因为它只会引用对象 y 中的成员 x。这可能会被其他线程更改。但我不确定这是否是真正的意图。