当写在一行中时,C++ move 构造函数不使用复合运算符 += 调用

标签 c++ operator-overloading move-semantics

我遵循了 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/

相关文章:

c++在win32学校应用程序上构建错误

c++ - 重用运算符函数返回的对象

c++ - 重载复制赋值运算符

c++ - 在 move 分配中使用 std::swap() 应该会导致无限递归(和原因),但它是 Stroustrup 书中的一个例子

c++ - 类型特征 C++ 的错误/错误使用

c++ - scanf 导致循环提前终止

c++ - 创建任务并返回值

c++ - 如何编写 C++ 转换运算符返回对数组的引用?

c++ - 我可以用移出的对象做什么?

c++ - 将范围拆分为子范围