7

I have the following code:

#include <iostream>
using namespace std;
void test(int& a) {
    cout << "lvalue." << endl;
}
void test(int&& a) {
    cout << "rvalue" << endl;
}
int main(int argc, char *argv[]) {
    int a = 1;
    int&& b = 2;
    test(a);
    test(1);
    test(std::move(a));
    test(b);
}

which outputs:

lvalue.
rvalue
lvalue.
lvalue.

std::move() and int&& are rvalue references, I wonder why test(std::move(a)) and test(b) output lvalue? Is it related with signature matching and function overloading?

ildjarn
  • 62,044
  • 9
  • 127
  • 211

2 Answers2

11

The output should be:

lvalue.
rvalue
rvalue
lvalue.

There is a very important distinction to be made between expressions which are rvalues and expressions whose type is an rvalue reference. The type of b is an rvalue reference to int, but the expression b is an lvalue; it is a variable, you can take its address. This is why the final line of output is lvalue rather than rvalue. In order to change it to an rvalue, you should call std::move on it:

test(std::move(b));
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Cool, you're right. The IDE I used ignores the "-std=c++11" option with the underlying command, such caused my output to be like what I've posted. I've tested the code in Terminal, if compiled without the "-std=c++11" option, then undesired output is given with warnings. – Simon X. M. LIU Jan 18 '16 at 06:26
1

You can read this article, which explains it very well Universal References in C++11. Also it worth mentioning that now these references called forwarding references.

In your case you have

void test(int& a); // lvalue reference overload
void test(int&& a); // rvalue reference overload

Second case allows you to implement move semantics or perfect forwarding inside the function. Though first one also allows it, only you need to use std::move which will turn its value to rvalue.

test(a);
test(1);
test(std::move(a));
test(b);
  • a has a name, so applying move semantics to it tacitly would be dangerously confusing and error-prone because the thing from which we just moved, is still accessible on subsequent lines of code.

  • 1 has no name, you can take the address of it, so it is an rvalue.

  • std::move(a) by using std::move you turn this to rvalue, you should remember it when you use a next time.

  • b the same as with a - it has a name, you can take the address of it.

Some examples of lvalues and rvalues:

// lvalues:
//
int i = 42;
i = 43; // ok, i is an lvalue
int* p = &i; // ok, i is an lvalue
int& foo();
foo() = 42; // ok, foo() is an lvalue
int* p1 = &foo(); // ok, foo() is an lvalue

// rvalues:
//
int foobar();
int j = 0;
j = foobar(); // ok, foobar() is an rvalue
int* p2 = &foobar(); // error, cannot take the address of an rvalue
j = 42; // ok, 42 is an rvalue
Yola
  • 18,496
  • 11
  • 65
  • 106
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/10890990) – Moha the almighty camel Jan 14 '16 at 13:55
  • 1
    @Mhd.Tahawi I've taken your advice to heart :) – Yola Jan 14 '16 at 14:24