简体   繁体   中英

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?

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().

What's wrong with sometype & a = foo(); ?

foo() returns temporary so you cannot bind it to reference because it will no longer exists after the end of full expression (assignment line). The only way to extend its life time is to change it to const sometype & a = foo(); Or assign it to rvalue reference.

Is sometype && b = foo(); actually rvalue reference?

yes (read here for more: Do rvalue references allow dangling references? )

Does it "steal" the return value from foo() and send what was in b to the destructor?

no, it extends its lifetime

Is there another way to not have this warning?

You have three choices: (1) assign to rvalue reference, (2) assign to const lvalue reference, (3) return by value but implement move semantics in your class.

You can also count on that compiler will perform RVO on returned value.

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