没有 std::move 的右值引用

Rvalue references without std::move

我关注class

class widget {
// The methods only print their name, i.e. c'tor, destructor etc.
public:
    widget();
    widget(const widget&);
    widget(widget&&);
    ~widget();
    auto operator=(const widget&)  -> widget&;
    auto operator=(widget&&) -> widget&;
};

我在下面的代码中使用了它

#include "widget.h"

auto main() -> int {

    widget c(std::move(widget()));
    c = std::move(widget());

    return 0;
};

由此产生的行为对我来说是可以理解的。在第一次调用中构造一个小部件,然后调用移动构造函数并在临时小部件上调用析构函数。

第二次调用做同样的事情,期望调用移动赋值运算符而不是移动构造函数。 离开 main 方法,在 c.

上调用析构函数

有趣的部分来了:

#include "widget.h"

auto main() -> int {

    widget c((widget()));
    c = widget();

    return 0;
};

如果我省略对 std::move 的调用,第一种情况将停止工作并导致仅调用一次构造函数。而第二种情况仍然像以前一样工作。

我在这里错过了什么?为什么这两个函数调用以不同方式处理它们的参数? 我在 gcc 和 clang 上试过了。

widget() 是一个纯右值 (prvalue),所以在行

widget c((widget())); // use widget c{widget()} or c{widget{}} for more clear code

它将被移动。然而,编译器只是执行copy/move elision。使用 -fno-elide-constructors 进行编译,您将看到对移动构造函数的所有调用。

无论何时显式使用 std::move 移动纯右值,都不允许编译器执行省略;这就是为什么您会在第一个片段中看到正在运行的移动构造函数。这就是为什么通过使用 std::move 作为 return 来尝试 "help" 编译器几乎总是一个坏主意(除非你真的想要 return 右值引用)。