It's safe? Rvalues ​​and reference return

#include <iostream>
#include <string>

using namespace std;

class Wrapper
{
public:
    std::string&& get() &&
    {
        std::cout << "rvalue" << std::endl;
        return std::move(str);
    }

    const std::string& get() const &
    {
        std::cout << "lvalue" << std::endl;
        return str;
    }
private:
    std::string str;
};

std::string foo()
{
    Wrapper wrap;
    return wrap.get();
}

std::string bar()
{
    Wrapper wrap;
    return std::move(wrap.get());
}

std::string fooRvalue()
{
    return Wrapper().get();
}

std::string barRvalue()
{
    return std::move(Wrapper().get());
}

int main() {
    while(1)
    {
        std::string s = foo();
        std::string s2 = bar();
        std::string s3 = fooRvalue();
        std::string s4 = barRvalue();
    }
    return 0;
}

      

Is the return value const std :: string &, and std :: string && & safe in this use case (pretend std :: string is another type that cannot be copied). I thought it wouldn't work because the refernce is supposed to point to some local variable that is out of scope, but it seems to work fine? Also I've seen the && syntax used before, did I use it correctly (I've limited it when the Wrapper itself is an rvalue).

I'm just a little confused when I return a link, it's safe, I thought the rule was that the object should have survived the return value, but I've seen things like the standard library and have previously validated a restore link. If I store the reference they give me in a variable (it's not a reference), everything seems to work fine if I return that stored variable. Can someone correct me and give me some good rules to follow.

Here's the ideon I was playing with, it all works: http://ideone.com/GyWQF6

+3


source to share


2 answers


Yes, this is the correct and safe way to implement a getter member. But you can make one improvement, which I will come to. The key is that the lifetime of a member is std::string

(almost) the same as the lifetime of the object Wrapper

that contains it. And the temporary is destroyed only at the end of the full expression, where it appears, and not immediately after its "use". Thus, in return std::move(Wrapper().get());

is created Wrapper

, invoked get()

, invoked std::move

, the return value is returned, and only then is Wrapper

it string

destroyed.

The only danger is that someone is linking another link to your member. But that would be their mistake, and regular constant reference getters have the same danger. This is equivalent to being wrong const char* ptr = "oops"s.c_str();

.

std::string foo()
{
    Wrapper wrap;
    return wrap.get();
}

      

foo

copy creates a return value from a member. I hope this is not a surprise.

std::string bar()
{
    Wrapper wrap;
    return std::move(wrap.get());
}

      

bar

also copies the return construct from the member. What happens here, since it wrap

is an lvalue, the lvalue is called get()

, which returns the const

lvalue. (Informally " const std::string&

".) Then move

converts that value to x, but it's still const

. (Informally, const std::string&&

".) The move constructor string(string&&)

here cannot be the same as an argument const

. But the copy constructor string(const string&)

can be the same and called.

If you were expecting to bar

move the return construct, you can add a third overload for non-const lvalues:



std::string& get() & { return str; }

      

(If you don't want to allow the member to change, note that you've already done so. For example, someone might have done it std::move(wrap).get() = "something";

.)

Also, if you had return std::move(wrap).get();

, then it would result in the construction of the return value even without the third overload.

std::string fooRvalue()
{
    return Wrapper().get();
}

      

fooRvalue

move creates a return value because rvalue is used get()

. As already mentioned, it Wrapper

lives long enough for this structure to be safe.

std::string barRvalue()
{
    return std::move(Wrapper().get());
}

      

The barRvalue

challenge is move

completely useless. The expression is Wrapper().get()

already the value of x, so it move

just converts the value of x of type std::string

to ... value of x of type std::string

. However, just as safe.

+3


source


The return value object of the function will be initialized before destroying the local variables and temporary files created in the expression return

. Thus, it is perfectly legal to move from locals / temporaries to function return values.

These four global functions work because they return objects, not object references. And this object is instantiated before any of the LANs are destroyed by their constructor reference.

barRvalue

is rather redundant (since the result is Wrapper().get()

already an rvalue reference), but functional.



I'm just a little confused when returning a link safely

A function that returns a reference to a destroyed object is unsafe. Returning a reference to a local object or part of a local object is unsafe.

Returning a reference to *this

or some part is *this

safe because the object this

will go into a function that returns a reference. This is no different than returning a reference to a parameter that you accept by pointer / reference. The object will go into a function call that returns a reference.

+3


source







All Articles