是否存在返回 RValue 引用 (&&) 有用的情况?

发布于 2024-11-02 23:30:13 字数 388 浏览 1 评论 0原文

函数应该返回 RValue 引用是否有原因?一种技巧、技巧、习语或模式?

MyClass&& func( ... );

我知道一般情况下返回参考文献的危险,但有时我们会这样做无论如何,不​​是吗? T& T::operator=(T) 只是一个惯用的示例。但是T&& 怎么样?函数(...)?这样做有什么普遍的地方可以让我们受益吗?与仅编写客户端代码相比,编写库或 API 代码时可能会有所不同?

Is there a reason when a function should return a RValue Reference? A technique, or trick, or an idiom or pattern?

MyClass&& func( ... );

I am aware of the danger of returning references in general, but sometimes we do it anyway, don't we? T& T::operator=(T) is just one idiomatic example. But how about T&& func(...)? Is there any general place where we would gain from doing that? Probably different when one writes library or API code, compared to just client code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

花开柳相依 2024-11-09 23:30:13

有一些场合是合适的,但相对较少。当您想要允许客户端从数据成员移动时,就会出现这种情况。例如:

template <class Iter>
class move_iterator
{
private:
    Iter i_;
public:
    ...
    value_type&& operator*() const {return std::move(*i_);}
    ...
};

There are a few occasions when it is appropriate, but they are relatively rare. The case comes up in one example when you want to allow the client to move from a data member. For example:

template <class Iter>
class move_iterator
{
private:
    Iter i_;
public:
    ...
    value_type&& operator*() const {return std::move(*i_);}
    ...
};
拧巴小姐 2024-11-09 23:30:13

这是对 towi 评论的跟进。您永远不想返回对局部变量的引用。但您可能会遇到这样的情况:

vector<N> operator+(const vector<N>& x1, const vector<N>& x2) { vector<N> x3 = x1; x3 += x2; return x3; }
vector<N>&& operator+(const vector<N>& x1, vector<N>&& x2)    { x2 += x1; return std::move(x2); }
vector<N>&& operator+(vector<N>&& x1, const vector<N>& x2)    { x1 += x2; return std::move(x1); }
vector<N>&& operator+(vector<N>&& x1, vector<N>&& x2)         { x1 += x2; return std::move(x1); }

这应该可以防止在所有情况下进行任何复制(以及可能的分配),除非两个参数都是左值。

This follows up on towi's comment. You never want to return references to local variables. But you might have this:

vector<N> operator+(const vector<N>& x1, const vector<N>& x2) { vector<N> x3 = x1; x3 += x2; return x3; }
vector<N>&& operator+(const vector<N>& x1, vector<N>&& x2)    { x2 += x1; return std::move(x2); }
vector<N>&& operator+(vector<N>&& x1, const vector<N>& x2)    { x1 += x2; return std::move(x1); }
vector<N>&& operator+(vector<N>&& x1, vector<N>&& x2)         { x1 += x2; return std::move(x1); }

This should prevent any copies (and possible allocations) in all cases except where both parameters are lvalues.

无人问我粥可暖 2024-11-09 23:30:13

不,只需返回值即可。一般来说,返回引用一点也不危险——它返回的是对本地变量的引用,这是危险的。然而,返回右值引用在几乎所有情况下都是毫无价值的(我猜如果你正在编写 std::move 或其他东西)。

No. Just return the value. Returning references in general is not at all dangerous- it's returning references to local variables which is dangerous. Returning an rvalue reference, however, is pretty worthless in almost all situations (I guess if you were writing std::move or something).

凡间太子 2024-11-09 23:30:13

如果您确定引用的对象在函数退出后不会超出范围,则可以通过引用返回,例如,它是全局对象的引用,或返回对类字段的引用的成员函数等。

此返回引用规则与两者相同左值和右值引用。区别在于您要如何使用返回的引用。正如我所看到的,通过右值引用返回的情况很少见。如果你有函数:

Type&& func();

你不会喜欢这样的代码:

Type&& ref_a = func();

因为它有效地将 ref_a 定义为 Type&由于命名右值引用是左值,因此此处不会执行实际移动。这很像:

const Type& ref_a = func();

除了实际的 ref_a 是非常量左值引用。

即使您直接将 func() 传递给另一个采用 Type&& 的函数,它也不是很有用。参数,因为它仍然是该函数内的命名引用。

void anotherFunc(Type&& t) {
  // t is a named reference
}
anotherFunc(func());

func( ) 和 anotherFunc( ) 的关系更像是一种“授权”,即 func() 同意 anotherFunc( ) 可以拥有(或者你可以说“窃取”)从 func( ) 返回的对象的所有权。但这个协议非常宽松。非常量左值引用仍然可以被调用者“窃取”。实际上函数很少被定义为接受右值引用参数。最常见的情况是“anotherFunc”是一个类名,而anotherFunc()实际上是一个移动构造函数。

You can return by reference if you are sure the referenced object will not go out of scope after the function exits, e.g. it's a global object's reference, or member function returning reference to class fields, etc.

This returning reference rule is just same to both lvalue and rvalue reference. The difference is how you want to use the returned reference. As I can see, returning by rvalue reference is rare. If you have function:

Type&& func();

You won't like such code:

Type&& ref_a = func();

because it effectively defines ref_a as Type& since named rvalue reference is an lvalue, and no actual move will be performed here. It's quite like:

const Type& ref_a = func();

except that the actual ref_a is a non-const lvalue reference.

And it's also not very useful even you directly pass func() to another function which takes a Type&& argument because it's still a named reference inside that function.

void anotherFunc(Type&& t) {
  // t is a named reference
}
anotherFunc(func());

The relationship of func( ) and anotherFunc( ) is more like an "authorization" that func() agrees anotherFunc( ) might take ownership of (or you can say "steal") the returned object from func( ). But this agreement is very loose. A non-const lvalue reference can still be "stolen" by callers. Actually functions are rarely defined to take rvalue reference arguments. The most common case is that "anotherFunc" is a class name and anotherFunc( ) is actually a move constructor.

錯遇了你 2024-11-09 23:30:13

另一种可能的情况是:当您需要解压元组并将值传递给函数时。

如果您不确定复制省略,它在这种情况下可能很有用。

这样的例子:

template<typename ... Args>
class store_args{
    public:
        std::tuple<Args...> args;

        template<typename Functor, size_t ... Indices>
        decltype(auto) apply_helper(Functor &&f, std::integer_sequence<size_t, Indices...>&&){
            return std::move(f(std::forward<Args>(std::get<Indices>(args))...));
        }

        template<typename Functor>
        auto apply(Functor &&f){
            return apply_helper(std::move(f), std::make_index_sequence<sizeof...(Args)>{});
        }
};

非常罕见的情况,除非您正在编写某种形式的 std::bind 或 std::thread 替换。

One more possible case: when you need to unpack a tuple and pass the values to a function.

It could be useful in this case, if you're not sure about copy-elision.

Such an example:

template<typename ... Args>
class store_args{
    public:
        std::tuple<Args...> args;

        template<typename Functor, size_t ... Indices>
        decltype(auto) apply_helper(Functor &&f, std::integer_sequence<size_t, Indices...>&&){
            return std::move(f(std::forward<Args>(std::get<Indices>(args))...));
        }

        template<typename Functor>
        auto apply(Functor &&f){
            return apply_helper(std::move(f), std::make_index_sequence<sizeof...(Args)>{});
        }
};

pretty rare case unless you're writing some form of std::bind or std::thread replacement though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文