简体   繁体   中英

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? 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. 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:

  1. What does std::move(object).funcname() mean?
  2. What does Typename().funcname() mean?

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. 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".

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 . And yet, if you pass it a tuple&& , you will get a T&& returned and not a T . Why?

Because you're accessing a member of the tuple .

If you had a struct , then std::move(struct_object).x would be an rvalue reference as well. So std::get is simply behaving in the same way for a tuple as member access would for a struct . That's kinda the whole point of tuple , after all: to behave like a struct as much as possible.

This allows you to do things like std::get<0>(std::move(tpl)).member , such that member will still be an rvalue reference. 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. 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.

It all depends on what matters more to you: safety or orthogonality with member accessors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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