Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding the warning: binding r-value to l-value reference

I want to pass a struct by reference so it won't be copied, but Resharper is giving the warning below:

struct sometype {
};

sometype foo() {
    sometype x;
    return x;
}

void bar() {
    sometype & a = foo();//Binding r-value to l-value reference is non-standard Microsoft C++ extension
    sometype && b = foo(); //ok
}

Questions:

What's wrong with sometype & a = foo(); ? isn't the return value from foo() an lvalue and a is also an lvalue?

Is sometype && b = foo(); actually rvalue reference? Does it "steal" the return value from foo() and send what was in b to the destructor?

Is there another way to not have this warning?

like image 362
shinzou Avatar asked Dec 12 '15 14:12

shinzou


1 Answers

You are taking a reference to a temporary object. The only legal way to do this is either :

const object& (const l-value reference), or

object&& (mutable r-value reference)

This is a (deliberate) language limitation.

further discussion:

Assigning a temporary to a reference extends the lifetime of the temporary so that it matches the lifetime of the reference. Therefore, surprisingly to many beginners, this is legal:

{
  const string& s = foo();
  cout << s << endl;         // the temporary to which s refers is still alive
}
// but now it's destroyed

However, it would normally be a logic error to take a mutable reference to a temporary so this is disallowed in the language:

{
  string s& = foo();  // this is not possible
  s += "bar";         // therefore neither is this
  // the implication is that since you modified s, you probably want to
  // preserve it
}
// ... but now it's destroyed and you did nothing with it.

here's a more realistic reason why it's probably a logic error, given:

string foo();         // function returning a string
void bar(string& s);  // this function is asserting that it intends to *modify*
                      // the string you sent it

// therefore:

bar(foo());           // makes no sense. bar is modifying a string that will be discarded.
                      // therefore assumed to be a logic error

you would have to replace the above with:

  string s = foo();
  s += "bar";
  // do something here with s

Note that there is no overhead whatsoever for capturing the temporary in a named variable (l-value).

r-value references are designed to be the subject of a move-constructor or move-assignment. Therefore it makes sense that they are mutable. Their very nature implies that the object is transient.

thus, this is legal:

string&& s = foo();    // extends lifetime as before
s += "bar";
baz(std::move(s));     // move the temporary into the baz function.

It might help you to remember that specifying && is you asserting that you know that the variable is a mutable temporary.

But the real reason it's allowed is so that this will work:

string foo();   // function that returns a string
void bar(string&& s);  // function that takes ownership of s

bar(foo());  // get a string from foo and move it into bar

// or more verbosely:

string s = foo();
bar(move(s));

prior to c++11, bar would have to have been written one of these ways:

void bar(string s);   // copy a string

// resulting in:

const string& s = foo();
bar(s);  // extra redundant copy made here

void bar(const string& s); // const l-value reference - we *may* copy it
// resulting in:

const string& s = foo();
bar(s);  // maybe an extra redundant copy made here, it's up to bar().
like image 177
Richard Hodges Avatar answered Oct 18 '22 13:10

Richard Hodges