22

Could anyone explain the details in terms of rvalues, lvalues, PODs, and non-PODs the reason why the first expression marked below is not ok while the second expression marked below is ok? In my understanding both int() and A() should be rvalues, no?


struct A {};

int main()
{
  int i;
  A a;

  int() = i; //Not OK (error).
  A() = a; //OK.

  return 0;
}

Jonas
  • 121,568
  • 97
  • 310
  • 388
anonymous
  • 231
  • 2
  • 4

3 Answers3

19

Rvalues are what you get from expressions (a useful simplification taken from the C standard, but not worded in C++ standardese). Lvalues are "locator values". Lvalues can be used as rvalues. References are always lvalues, even if const.

The major difference of which you have to be aware can be condensed to one item: you can't take the address of an rvalue (again, not standardese but a useful generalization of the rules). Or to put it another way, you can't fix a precise location for an rvalue—if you could, then you'd have an lvalue. (You can, however, bind a const& to an rvalue to "fix it in place", and 0x is changing the rules drastically.)

User-defined types (UDTs), however, are slightly special: you can convert any rvalue into an lvalue, if the class's interface allows it:

struct Special {
  Special& get_lvalue() { return *this; }
};
void f() {
  // remember "Special()" is an rvalue
  Special* p = &Special().get_lvalue(); // even though you can't dereference the
  // pointer (because the object is destroyed), you still just took the address
  // of a temporary

  // note that the get_lvalue() method doesn't need to operate on a const
  // object (though that would be fine too, if the return type matched)
}

Something similar is happening for your A() = a, except through the compiler-supplied assignment operator, to turn the rvalue A() into *this. To quote the standard, 12.8/10:

If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. The implicitly-declared copy assignment operator for a class X will have the form

X& X::operator=(const X&)

And then it goes on with more qualifications and specs, but that's the important bit here. Since that's a member function, it can be called on rvalues, just like Special::get_lvalue can be, as if you had written A().operator=(a) instead of A() = a.

The int() = 1 is explicitly forbidden as you discovered, because ints don't have operator= implemented in the same way. However, this slight discrepancy between types doesn't matter in practice (at least not that I've found).


POD means Plain Old Data and is a collection of requirements that specify using memcpy is equivalent to copying. Non-POD is any type for which you cannot use memcpy to copy (the natural opposite of POD, nothing hidden here), which tends to be most types you'll write in C++. Being POD or non-POD doesn't change any of the above, and is really a separate issue.

  • C-specific, but still useful for C++ programmers to understand: http://stackoverflow.com/questions/2038414/lvalue-and-rvalue/2038427#2038427. (This is one area both languages fundamentally share, even though C++ is quite a bit more complicated with references and actually using the term "rvalue" which C doesn't anymore.) –  Feb 19 '10 at 04:32
  • "_Or to put it another way, you can't fix a precise location for an rvalue_" according to the language standard, a rvalue of fundamental type isn't even an object, and doesn't have an address. – curiousguy Dec 25 '11 at 15:25
2

In my understanding both int() and A() should be rvalues, no?

Correct, the epxression T() is always an rvalue for scalar and user-defined types T. As long as no const is involved, the expression T() is a modifiable rvalue, to be more precise.

Assignment involving scalar types requires a modifiable lvalue on the left hand side of the assignment operator. Since int() isn't an lvalue, you can't assign to int().

For user-defined types, assignment is a special member function, and member functions can also be called on rvalues (see §3.10 section 10). That's why A().operator=(a) is well formed.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • `int()` is a _modifiable rvalue_? – curiousguy Dec 25 '11 at 15:27
  • @curiousguy: Yes. [There are no const scalar rvalues](http://stackoverflow.com/questions/2169932/). – fredoverflow Dec 25 '11 at 22:09
  • 1
    @curiousguy: Don't worry, there is no way to modify a modifiable scalar rvalue. Don't let the names mislead you ;) "modifiable" is just standardese for "non-const", and since scalar rvalues are never const, they are modifiable, even though you cannot modify them. – fredoverflow Dec 25 '11 at 22:46
  • 1
    Well, IMO that's slightly vicious. – curiousguy Dec 25 '11 at 22:55
  • 1
    @curiousguy: What would be the difference between a function returning an `int` and a function returning a `const int`? People figured the `const` doesn't add anything here, and so it is simply ignored. – fredoverflow Dec 25 '11 at 23:53
  • Still, it's strange that the result of that function is said to be "modifiable". – curiousguy Dec 26 '11 at 00:01
1

From Does C++ do value initialization of a POD typedef?, which quotes the Standard:

The expression T(), where T is a simple-type-specifier (7.1.5.2) for a non-array complete object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, which is value-initialized

Therefore int() is an rvalue and can't be assigned to, as you saw in your first case.

A() would not be a simlle-type-specifyer and thus A() yields an lvalue

Community
  • 1
  • 1
DVK
  • 126,886
  • 32
  • 213
  • 327