14

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

#include <utility>


struct Bar
{
};

struct Foo
{
    Bar bar;

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

3 回答 3

19

好吧,既然它是一个 r 值 ref 限定的成员函数,this大概就要过期了。所以搬出去是有道理的bar,假设Bar实际上从搬家中获得了一些东西。

由于bar是成员,而不是本地对象/函数参数,因此返回语句中复制省略的常用标准不适用。除非您明确说明,否则它将始终复制std::move

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

于 2018-01-28T11:40:21.723 回答
4

我更喜欢选项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-ingbar是正确的。但仅仅因为我们对移动持开放态度bar并不意味着我们必须强制执行这样的移动并招致额外的操作,所以我们应该只返回一个对它的右值引用。

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

于 2018-01-29T16:16:39.620 回答
1

我想澄清我的观点(来自评论)。尽管移动结果通常比复制效率高得多,但这不是我主要关心的问题。核心问题源于错误假设,即通过在 r 值引用上调用此方法来引用Foo实例调用者的意图,包括创建新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 并控制存储的 bar 对象的内容。

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;

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

于 2018-01-28T12:57:36.420 回答