12

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?

shinzou
  • 5,850
  • 10
  • 60
  • 124

2 Answers2

15

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().
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Is `object&&` considered rvalue reference? – shinzou Dec 12 '15 at 14:36
  • Also what is motivation behind adding the const to `const object&`? – shinzou Dec 12 '15 at 14:36
  • Yes. In this context it's an r-value reference. – Richard Hodges Dec 12 '15 at 14:37
  • 3
    The original thinking is that taking a mutable reference to a temporary makes no sense, since the temporary will be thrown away. Calling mutable methods on a temporary would normally be a logic error so it's disallowed. Specifying an r-value reference signals essentially that you know what you're doing. – Richard Hodges Dec 12 '15 at 14:40
  • I see you've added more examples, in the code snippet one before last, is `bar(foo());` the infamous stealing of rvalue reference? – shinzou Dec 12 '15 at 15:25
  • 1
    I didn't know it was infamous, but it certainly wasn't possible before c++11 :) – Richard Hodges Dec 12 '15 at 17:39
  • 1
    In your line `string s& = foo(); // this is not possible`, do you mean `string &s = foo(); // this is not possible`? – phinz Jan 30 '23 at 09:22
5

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.

Community
  • 1
  • 1
marcinj
  • 48,511
  • 9
  • 79
  • 100