Search code examples
c++c++11rvalue-referencetemporary-objects

rvalue for a std::string parameter


What's the difference in practice between LVALUE and RVALUE in the following code when I pass the text? I mean, in this specific case of a string (where the string is a string literal), is there any benefit of using RVALUE (&&)?

void write_Lvalue(const std::string &text) {
    //...
}

void write_Rvalue(const std::string &&text) {
    //...
}

int main() {
    write_Lvalue("writing the Lvalue");
    write_Rvalue("writing the Rvalue");
}

Solution

  • First, constant rvalue reference are not really useful, since you cannot move them. Moving value need mutable references to work.

    Let's take your corrected example:

    void write_lvalue(std::string const& text) {
        //...
    }
    
    void write_rvalue(std::string&& text) {
        //...
    }
    
    int main() {
        write_lvalue("writing the Lvalue");
        write_rvalue("writing the Rvalue");
    }
    

    In this case, the two are completely equivalent. In these two case, the compiler has to create a string and send it by reference:

    int main() {
        // equivalent, string created
        // and sent by reference (const& bind to temporaries)
        write_lvalue(std::string{"writing the Lvalue"}); 
    
        // equivalent, string created
        // and sent by reference (&& bind to temporaries)
        write_rvalue(std::string{"writing the Rvalue"});
    }
    

    So why have function that takes rvalue references?

    It depends on what you do with the string. A mutable reference can be moved from:

    std::string global_string;
    
    void write_lvalue(std::string const& text) {
        // copy, might cause allocation
        global_string = text;
    }
    
    void write_rvalue(std::string&& text) {
        // move, no allocation, yay!
        global_string = std::move(text);
    }
    

    So why using rvalue reference at all? Why not using mutable lvalue reference?

    That is because mutable lvalue references cannot be bound to temporaries:

    void write_lvalue_mut(std::string& text) {
        // move, no allocation... yay?
        global_string = std::move(text);
    }
    
    int main() {
        std::string s = /* ... */;
        write_lvalue_mut(std::move(s)); // fails
        write_lvalue_mut("some text"); // also fails
    }
    

    But mutable rvalue reference can be bound to rvalue, as shown above.