当写在一行中时,C++ 移动构造函数不使用复合运算符 += 调用
C++ Move constructor not called with the compound operator += when written in one line
我遵循了 Whosebug 中有关 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
为什么第二种情况确实给出了预期的结果,正确使用了移动语义,但第一种情况却到处复制?
我只想补充一点,第二个场景是我阅读的教程中推荐的场景(如上面的 link),但是当我尝试实现它时,我凭直觉写了第一个场景并且它给了我错误的行为...
从具有相同1 return 类型 T
is a special case 的函数返回类型 T
的局部变量.
它至少会自动移动变量,或者,如果编译器足够聪明,可以执行 so-called NRVO,则完全消除 copy/move 并直接在正确的位置构造变量。
函数参数(与常规局部变量不同)不符合 NRVO 的条件,因此您总是在 (2) 中得到隐式移动。
这不会发生在 (1) 中。编译器不会分析 +=
来理解它 return 的内容;此规则仅在 return
的操作数是单个变量时有效。
由于 +=
return 是一个左值引用,而您没有 std::move
它,因此调用了复制构造函数。
1 或仅 cv-qualifiers.
不同的类型
我遵循了 Whosebug 中有关 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
为什么第二种情况确实给出了预期的结果,正确使用了移动语义,但第一种情况却到处复制?
我只想补充一点,第二个场景是我阅读的教程中推荐的场景(如上面的 link),但是当我尝试实现它时,我凭直觉写了第一个场景并且它给了我错误的行为...
从具有相同1 return 类型 T
is a special case 的函数返回类型 T
的局部变量.
它至少会自动移动变量,或者,如果编译器足够聪明,可以执行 so-called NRVO,则完全消除 copy/move 并直接在正确的位置构造变量。
函数参数(与常规局部变量不同)不符合 NRVO 的条件,因此您总是在 (2) 中得到隐式移动。
这不会发生在 (1) 中。编译器不会分析 +=
来理解它 return 的内容;此规则仅在 return
的操作数是单个变量时有效。
由于 +=
return 是一个左值引用,而您没有 std::move
它,因此调用了复制构造函数。
1 或仅 cv-qualifiers.
不同的类型