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

后端 未结 2 1956
走了就别回头了
走了就别回头了 2020-12-16 19:05

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         


        
相关标签:
2条回答
  • 2020-12-16 19:49

    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.

    0 讨论(0)
  • 2020-12-16 19:52

    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().
    
    0 讨论(0)
提交回复
热议问题