简体   繁体   English

在&&限定函数中返回右值引用和值之间的区别

[英]Difference between returning an rvalue reference and a value in && qualified functions

I saw this question recently Is returning by rvalue reference more efficient? 我最近看到了这个问题通过右值引用返回是否更有效? and also noticed the comments chain. 并且注意到了评论链。 I am following up on the comments chain for the answer there. 我正在评论链中寻找答案。

People seem to say that in the case below the return value should be by rvalue reference and not by value. 人们似乎说,在以下情况下,返回值应按右值引用而不是按值。 ( Is returning by rvalue reference more efficient? and Is there any case where a return of a RValue Reference (&&) is useful? ) Given the following code 通过右值引用返回是否更有效?并且在任何情况下, 返回右值引用 (&&)都是有用的吗? )给出以下代码

#include <iostream>

using namespace std;

class Something {
public:
    Something() {
        cout << "Something()" << endl;
    }
    Something(const Something&) {
        cout << "Something(const Something&)" << endl;
    }
    Something(Something&&) {
        cout << "Something(Something&&)" << endl;
    }
};

class Maker {
public:
    Something get_something() && {
        return std::move(this->something);
    }
    const Something& get_something() & {
        return this->something;
    }

private:
    Something something;
};

int main() {
    auto maker = Maker{};
    auto something_one = maker.get_something();
    auto something_two = Maker{}.get_something();

    return 0;
}

What is the difference between defining the first method in the class Maker with an rvalue reference return type and a regular value? Maker类中使用右值引用返回类型和常规值定义第一个方法有什么区别? Running the code above gives me the following output as expected (the move happens in the function call and after the return the move is elided) 运行上面的代码可以按预期提供以下输出(该移动发生在函数调用中,返回之后该移动被忽略)

Something()
Something(const Something&)
Something()
Something(Something&&)

And when I change the code (ie the first method in the Maker class) to return an rvalue reference I still get the same output. 当我更改代码(即Maker类中的第一个方法)以返回右值引用时,我仍然得到相同的输出。 Why shouldn't I just return by value in that case? 在这种情况下,为什么不只按价值返回?

When you use this reference qualifiers in this way, you have to ask yourself two questions: 当您以这种方式使用this参考限定词时,您必须问自己两个问题:

  1. What does std::move(object).funcname() mean? std::move(object).funcname()是什么意思?
  2. What does Typename().funcname() mean? Typename().funcname()是什么意思?

If you return a value from the function, then both of those will mean the same thing. 如果从函数返回一个值,则这两个都将表示同一件事。 Regardless of what you do to capture the value, the value will be a whole and distinct object, move-constructed from some internal data stored in the object. 不管您执行什么操作来捕获值,值都将是一个完整且与众不同的对象,它是由存储在对象中的一些内部数据移动构造而成的。

In the first case, object now potentially no longer owns the data. 在第一种情况下, object现在可能不再拥有数据。 In the second case, it doesn't matter because the object was a temporary and has since been destroyed. 在第二种情况下,这并不重要,因为该对象是临时对象,此后已被销毁。

If you return a && from the function, then those will mean different things. 如果从函数中返回&& ,那么它们将具有不同的含义。 Namely, #2 will mean "your code is broken". 即,#2表示“您的代码已损坏”。

As to why you might still want to do it, even if it allows broken code. 至于为什么您仍然想这样做,即使它允许代码损坏也是如此。 Well, that has to do with the actual answers to that question: what do those things mean ? 好吧,这与该问题的实际答案有关:这些东西什么意思

Here's what I am referring to. 这就是我所指的。 std::get is basically a member function of tuple . std::get基本上是tuple的成员函数。 And yet, if you pass it a tuple&& , you will get a T&& returned and not a T . 但是,如果将其传递给tuple&& ,则会得到T&&返回,而不是T Why? 为什么?

Because you're accessing a member of the tuple . 因为您正在访问tuple成员

If you had a struct , then std::move(struct_object).x would be an rvalue reference as well. 如果您具有struct ,则std::move(struct_object).x也将是右值引用。 So std::get is simply behaving in the same way for a tuple as member access would for a struct . 因此, std::get对于tuple行为与对struct成员访问的行为相同。 That's kinda the whole point of tuple , after all: to behave like a struct as much as possible. 毕竟,这就是tuple的全部要点:尽可能表现得像一个struct

This allows you to do things like std::get<0>(std::move(tpl)).member , such that member will still be an rvalue reference. 这使您可以执行std::get<0>(std::move(tpl)).member ,这样该member仍将是右值引用。 So you can move from a subobject without disturbing the rest of the object, exactly as you could for any other rvalue reference member accesses. 因此,您可以从子对象移动而不会干扰对象的其余部分 ,这与您对任何其他右值引用成员的访问一样。

If get returned a value, then this would do something very different. 如果get返回一个值,那么这样做会大为不同。 Regardless of what we do with the return value, it will be moved out of the object, no questions asked. 无论我们如何处理返回值,它都将移出对象,而不会出现任何问题。 So if we only wanted to move a subobject of that member... too bad. 因此,如果我们只想移动该成员的子对象,那就太糟糕了。 The original object lost the entire member, not just a subobject of that member. 原始对象失去了整个成员,而不仅仅是该成员的子对象。

Of course, that doesn't change the fact that: 当然,这不会改变以下事实:

auto &&x = SomeStruct().x; //This extends the temporary's lifetime
auto &&x = std::get<0>(SomeTuple(...)); //This gets a dangling reference.

That is an unfortunate limitation of the language. 这是语言的不幸限制。 But if the function is logically a member accessor, then returning a && from a && qualified this function instead of a value is a legitimate choice. 但是,如果该函数是逻辑上的成员访问,然后返回一个&&&&胜任this功能,而不是一个值是合理的选择。

It all depends on what matters more to you: safety or orthogonality with member accessors. 这完全取决于对您而言更重要的事情:安全性或与成员访问器的正交性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM