有没有理由将返回值捕获为右值参考?

时间:2016-11-23 18:41:31

标签: c++ c++11 move

我有一个仅移动的结构Foo和一个函数Foo get();

如果我想以可变的方式捕获get的返回值,我有两个选择:

  • 按值(Foo
  • by rvalue-reference(Foo&&

当我通过rvalue-reference捕获时,我创建一个左值,就像按值捕获一样。

我很难看到不同选项之间的关系?

  • 这两者之间是否存在任何差异?

工作示例:

#include <iostream>

struct Foo
{
    Foo(std::string s) : s(std::move(s)) {}
    Foo(Foo&& f)       : s(std::move(f.s)) {}

    Foo(const Foo&) = delete;
    Foo& operator=(const Foo&) = delete;

    std::string s;
};

Foo get()
{
    return Foo { "hello" };
}

int main()
{
    // capture return value as l-value
    Foo lv1 = get();

    // move into another lvalue
    Foo lv2 = std::move(lv1);
    std::cout << lv2.s << '\n';

    // capture return value as r-value reference
    Foo&& rv1 = get();

    // move into another lvalue
    Foo lv3 = std::move(rv1);
    std::cout << lv3.s << '\n';

    return 0;
}

2 个答案:

答案 0 :(得分:4)

Foo lv1 = get();

这要求Foo是可复制/可移动的。

Foo&& rv1 = get();

这不是(至少不是这行代码; get的实现可能仍然需要一个)。

即使允许编译器将返回值的副本遗漏到变量中,此表单的复制初始化仍然需要存在可访问的副本或移动构造函数。

因此,如果您希望对类型Foo施加尽可能少的限制,则可以存储返回值的&&

当然,C ++ 17会更改此规则,以便第一个不需要复制/移动构造函数。

答案 1 :(得分:3)

Foo&&

这将创建对临时值的引用(或存储分配给它的右值引用)。如果它存储对临时值的引用,则其生命周期会扩展该值。

Foo

无论返回什么内容,都会存储该值的副本。

在C ++ 11和14中,如果无法移动Foo,则将Foo make_foo()分配给Foo类型的变量是非法的。在那里有一个移动,即使移动被省略(并且返回值和外部范围中的值已经合并了生命周期)。

在C ++ 17中,保证省略意味着不需要移动构造函数。

Foo x = make_foo(); // Foo make_foo()

上面的C ++ 17保证make_foo()的返回值只是名为x。事实上, make_foo中的临时也可能是x;同一个对象,名称不同。不需要移动。

还有一些其他微妙的差异。 decltype(x)将返回声明的x类型;因此FooFoo&&取决于。

另一个重要区别是它与auto一起使用。

auto&& x = some_function();

这会创建对任何内容的引用。如果some_function返回临时值,它会绑定对它的右值引用并延长其生命周期。如果它返回引用,x匹配引用的类型。

auto x = some_function();

这会创建一个值,该值可以从some_function返回的内容中复制,如果返回临时值,则可以使用some_function的返回值进行删除。

从某种意义上说,

auto&&意味着&#34;只是让它发挥作用,而不是做额外的工作&#34;,它可以演绎为Foo&&auto表示&#34;存储副本&#34;。

在&#34;几乎总是自动&#34;风格,这些比明确的FooFoo&&更常见。

auto&&永远不会推断为Foo,但可以推断为Foo&&

auto&&的最常见用途,即使在几乎总是自动之外,也是:

for(auto&& x : range)

其中x成为迭代范围的有效方式,我们不关心range类型的内容。另一个常见用途是:

[](auto&& x){ /* some code */ }

lambda通常用于类型明显且不值得再次输入的上下文中,例如传递给算法等。通过将auto&&用于参数类型,我们可以使代码更简洁。