从 r-value ref-qualified 方法移动,还是不移动?

To move, or not to move from r-value ref-qualified method?

在下面的 C++11+ 代码中,哪个 return 语句构造应该是首选?

#include <utility>


struct Bar
{
};

struct Foo
{
    Bar bar;

    Bar get() &&
    {
        return std::move(bar); // 1
        return bar;            // 2
    }
};

嗯,因为它是一个 r-value ref 合格的成员函数,this 大概即将过期。所以将 bar 移出是有意义的,假设 Bar 实际上从移动中获得了一些东西。

由于 bar 是一个成员,而不是本地 object/function 参数,因此 return 语句中复制省略的通常标准不适用。它总是会复制,除非你明确地std::move它。

所以我的答案是选择第一个选项。

我想澄清我的观点(来自评论)。尽管移动结果通常比复制更有效,但这不是我主要关心的问题。核心问题源于错误的假设,即通过对 Foo 实例调用者的意图的 r 值引用调用此方法包括创建新的 Bar 值。例如:

Foo Produce_Foo(void);

// Alright, caller wanted to make a new `Bar` value, and by using `move`
// we've avoided a heavy copy operation.
auto bar{Produce_Foo().get()};

// Oops! No one asked us to make a useless temporary...
cout << Produce_Foo().get().value() << endl;

解决方案是添加一个专用函数,仅用于查看存储的栏并控制存储的栏对象的内容。

Bar const & get_bar() const noexcept
{
    return bar;
}

// no particular need to this method to be r-value reference qualified
// because there is no direct correlation between Foo instance being moved / temp
// and intention to take control over content of stored bar object.
Bar give_bar() noexcept
{
    return ::std::move(bar);
}

既然用户有了选择,就不会有更多问题了:

// Alright, caller wanted to make a new `Bar` value, and by using `move`
// we've avoided a heavy copy operation.
// There is also no need to figure out whether Produce_Foo returned an rvalue or not.
auto bar{Produce_Foo().give_bar()};

// Alright, no extra temporaries.
cout << Produce_Foo().get_bar().value() << endl;

至于右值引用限定方法的用例,我认为它们在处理与此对象相同类型的临时对象时最有用。例如string class 实现这种连接运算符可以减少 重新分配,本质上就像一个专用的字符串生成器。

我更喜欢选项 3:

Bar&& get() &&
// ^^
{
    return std::move(bar);
}

而且,当我们这样做的时候:

Bar& get() & { return bar; }
Bar const& get() const& { return bar; }
Bar const&& get() const&& { return std::move(bar); }

我们是右值,所以它应该可以自由蚕食我们的资源,所以 move-ing bar 是正确的。但是仅仅因为我们愿意移动 bar 并不意味着我们必须强制执行这样的移动并导致额外的操作,所以我们应该 return 一个右值引用它。

这就是标准库的工作方式 - 例如std::optional<T>::value.