4

My code is as follows:

#include <iostream>
using namespace std;

class A{
public:
    void sendByRvalue(string&& str){
        cout << str << endl;
    }
};

class B{
private:
    A a;
    void send(string&& str){
        a.sendByRvalue(str);
    }
public:
    void run(const string& str){
        send("run " + str + "\n");
    } 
}; 

int main(void){
    string str("hello world");
    B b;
    b.run(str);
    return 0;
}

When I compile the code shown above, I got some compile errors: enter image description here

It seems that the str in B::send function has been changed to lvalue. Then I change the implement of B::send, like:

class B{
private:
    A a;
    void send(string&& str){
        cout << boolalpha << is_rvalue_reference<decltype(str)>::value << endl;
        a.sendByRvalue(std::move(str));
    }
public:
    void run(const string& str){
        send("run " + str + "\n");
    } 
}; 

Everything goes well, but the output of this program made me confuse more. The output is as follows: enter image description here

Why the parameter str is a rvalue reference but I cannot pass it to the function A::sendByRvalue directly without std::move ?

Phoenix Chao
  • 390
  • 3
  • 19
  • 2
    Nice question. I'd suggest this though: For your compile errors it's better to have them in a written form so that others googling similar errors might find this question. To make it readable you can format it as a quote. For this question it doens't matter much, but it's a general rule on SO that the community dislikes pictures of text. – Elliott Nov 12 '20 at 05:38
  • Good suggestion! Let others can find this question by googling the same error message is a good idea. I have tried to changed these messages to written format. But it looks strange. So I have little choice but to paste those pictures – Phoenix Chao Nov 12 '20 at 05:59

2 Answers2

4

str is a named rvalue reference, which is treated in the language as an lvalue. rvalues are only xvalues or prvalues, and str is neither.

A note from the standard, on the xvalue rules:

In general, the effect of this rule is that named rvalue references are treated as lvalues and unnamed rvalue references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether named or not.

struct A {
int m;
};
A&& operator+(A, A);
A&& f();

A a;
A&& ar = static_cast<A&&>(a);

The expressions f(), f().m, static_­cast<A&&>(a), and a + a are xvalues. The expression ar is an lvalue.

Joel Filho
  • 1,300
  • 1
  • 5
  • 7
  • 3
    Oh, thanks a lot. I got your point. The key point is that the rvalue reference and the rvalue is not the same thing. So that's why the `is_rvalue_reference::value` is true while the `str` is lvalue. – Phoenix Chao Nov 12 '20 at 05:17
2

I'll make a simpler example to explain

void bar(std::string&& name) {
    std::cout << "Bar: " << name << std::endl;
}

void foo(std::string&& name) {
    bar(name);
}


int main() {
    foo("C++");
}

This is more or less the same as the example you have provided. Inside foo(), name is an lvalue. The temporary string C++, which is an rvalue is passed into name. name is an lvalue. So the above code is basically translated to.

void bar(std::string&& name) {
    std::cout << "Bar: " << name << std::endl;
}

int main()
{
    std::string foo{ "C++" };
    bar(foo);
}

Now it seems obvious where the problem is.


I wrote this simple program to help you understand better

void foo(const std::string& name) {
    std::cout << "Passed as lvalue ref: " << name << std::endl;
}

void foo(std::string&& name) {
    std::cout << "Passed as rvalue ref: " << name << std::endl;
}

We'll use this function to call

void bar(std::string&& name) {
    foo(name);
}

int main()  {
    bar("C++");
}

Passed as lvalue ref: C++

This proves my point, now what if we use std::move()?

void bar(std::string&& name) {
    foo(std::move(name));
}

int main()  {
    bar("C++");
}

Passed as an rvalue ref: C++

If you want a simple way to check whether an expression is an lvalue or rvalue, you can try the below snippet

template <typename T>
constexpr bool is_lvalue(T&) {
    return true;
}

template <typename T>
constexpr bool is_lvalue(T&&) {
    return false;
}

source