我遵循了 stackoverflow 中有关 Move 和 Operator 重载的精彩教程(例如 What are the basic rules and idioms for operator overloading? ),但以下情况让我感到困惑。代码中没有什么特别的,只是在调用特殊成员函数时打印。
主要代码:
class B {
public:
B() { std::cout << "B::ctor\n"; }
~B() { std::cout << "B::dtor\n"; }
B(B const &b) {
std::cout << "B::copy ctor\n";
}
B &operator=(B const &rhs) {
std::cout << "B::copy assignment\n";
return *this;
}
B(B &&b) {
std::cout << "B::move ctor\n";
}
B &operator=(B &&rhs) {
std::cout << "B::move assignment\n";
return *this;
}
B &operator+=(B const &rhs) {
std::cout << "B::operator+=\n";
return *this;
}
};
int main() {
B b;
std::cout << "=== b = b + b + b ===\n";
b = b + b + b;
}
现在,两个场景,在每个场景中,我对运算符 + 的定义不同:
B operator+(B p1, B const &p2) {
std::cout << "B::operator+\n";
return p1 += p2;
}
整个程序的输出:
B::ctor
=== b = b + b + b ===
B::copy ctor
B::operator+
B::operator+=
B::copy ctor
B::operator+
B::operator+=
B::copy ctor
B::move assignment
B::dtor
B::dtor
B::dtor
B::dtor
第二种情况:
B operator+(B p1, B const &p2) {
std::cout << "B::operator+\n";
p1 += p2;
return p1;
}
输出:
B::ctor
=== b = b + b + b ===
B::copy ctor
B::operator+
B::operator+=
B::move ctor
B::operator+
B::operator+=
B::move ctor
B::move assignment
B::dtor
B::dtor
B::dtor
B::dtor
为什么第二种情况确实给出了预期的结果,正确使用了 move 语义,但第一种情况却到处复制?
我只想补充一点,第二个场景是我阅读的教程中推荐的场景(如上面的链接),但是当我尝试实现它时,我凭直觉写了第一个场景,它给了我错误的行为...
最佳答案
从具有相同1 返回类型T
的函数返回类型T
的局部变量is a special case .
它至少会自动 move 变量,或者,如果编译器足够聪明以执行所谓的 NRVO,则完全消除复制/move 并直接在正确位置构造变量。
函数参数(与常规局部变量不同)不符合 NRVO 的条件,因此您总是会在 (2) 中获得隐式 move 。
这不会发生在 (1) 中。编译器不会分析 +=
来理解它返回的内容;此规则仅在 return
的操作数是单个变量时有效。
由于 +=
返回左值引用,而您没有std::move
它,因此调用了复制构造函数。
1 或仅在 cv 限定符方面不同的类型。
关于当写在一行中时,C++ move 构造函数不使用复合运算符 += 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71070239/