15

C++03 §4.2 N°1:
An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to an rvalue of type “pointer to T.” The result is a pointer to the first element of the array.

What has been confusing in this statement for a long time for me was that I didn't quite understand what an rvalue of array type would mean. That is, I couldn't come up with an expression whose type were an array and the result were an rvalue. I read this thread, which basically asks the same question and the accepted answer is "no, there is no rvalue of array type". I think I just might have a contradiction to this.

C++03 §5.2.5 N°4: (is about expression E1.E2)
If E2 is a non-static data member, and the type of E1 is “cq1 vq1 X”, and the type of E2 is “cq2 vq2 T”,the expression designates the named member of the object designated by the first expression. If E1 is an lvalue, then E1.E2 is an lvalue.

I assume that otherwise it is an rvalue (provided E2 is not a reference, that case is covered by §5.2.5 N°3) and therefore...

struct A
{
   int a[4];
};
A f()
{
   A a;
   return a; 
}
int main()
{
   f().a; //I think this is an rvalue of array type...
}

I see two options here:
Option1: I am correct, hurray, yay, cool. In this case the question is: are there other examples?
Option2: I am incorrect, in this case the question is: is this a defect of the standard?

I don't know about 1, but I really doubt about 2 because when they speak about function-to-pointer conversions they mention just lvalues of function types (obviously appreciating that there are no rvalues of such). So it's very likely they had thought abour rvalues of array types.

So, basically my question is whether or not I have come up with an example of rvalue of array type, and if not, please provide a valid one, which I stongly believe there exists.

Community
  • 1
  • 1
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434

2 Answers2

14

Yes, you are correct. The expression is an rvalue of array type. This is not a defect - the committee knows about it, and it was also a common issue in C89, which only allows conversion to pointers for lvalues of array types. As a consequence, you could not index or dereference an array like f().a. C99 fixed this, and C++ does not have a problem with it.

Note that whether or not it is an rvalue is independent to whether or not the expression denotes an object. C++03 accidentally omitted to say that an rvalue expression that is of array type denotes an object. This was fixed in C++0x by DR#450.

(obviously appreciating that there are no rvalues of such)

There are actually rvalues of function types. These occur for non-static member functions denoted by a class member access expression

struct A { void f(); };

/* A().f is an rvalue of type "void()" */
int main() { A().f(); }
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Thanks! So do you know if this is the only case of rvalue arrays, that is made by . or ->. Are there other examples? – Armen Tsirunyan Oct 30 '10 at 10:23
  • @Armen I can't think of another case (for C++03). – Johannes Schaub - litb Oct 30 '10 at 10:27
  • 1
    @Armen note that as a consequence, Comeau says that `A().a` is of type `int[4]`, even if you declared the array as `const int a[4];`. That's because the Standard says rvalues of non-class types are never cv-qualified, and because the EDG frontend takes cv-qualification of arrays and their element types as bidirectionally equivalent (see core issue #1059). – Johannes Schaub - litb Oct 30 '10 at 10:32
  • Excellent answer. +100 for the const example! And the function rvalue example. – Armen Tsirunyan Oct 30 '10 at 10:43
-3

The A is an rvalue. The array inside it isn't. Imagine the case in which you have a method chain on that temporary object - the variables within it live for more than one method call and return, and they may pass references (valid for the duration of the chain) to other functions. Those functions cannot know in advance that they shall be called upon an rvalue.

In the latest version of the draft, you can overload functions on rvalue/lvalue *this. However, even then, an rvalue reference does not make the contents of what is referred to an rvalue, and I'm not entirely sure that ANY compiler currently supports this, and I know that MSVC doesn't.

Infact, using decltype, you can determine easily that the compiler calls that array an lvalue.

Consider:

template<typename A, typename B> auto sum(A&& a, B&& b) -> decltype(std::forward<A>(a) + std::forward<B>(b)) {
    return std::forward<A>(a) + std::forward<B>(b);
}

That's what decltype was for, and it most definitely differentiates between lvalues and rvalues. Alternatively, consider this:

int main()
{
    auto var = f().a;
}

Var is an int*. That's an instant fail, since f().a immediately dies. Not sure of my immediate opinion on that, but it's certainly not valid for an rvalue.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • decltype does not differentiate between rvalues and lvalues. and please read my second quote from the standard once more. – Armen Tsirunyan Oct 30 '10 at 10:17
  • @Armen: The entire point of decltype is to differentiate. How else could perfect forwarding even begin to work? – Puppy Oct 30 '10 at 10:42
  • 2
    @DeadMG decltype(expr) is the type of the expr. whether or not expr is an lvalue or rvalue has nothing to do with its type. – Armen Tsirunyan Oct 30 '10 at 10:44
  • 2
    -1 @DeadMG: nope. The rvalue/lvalue distinction applies to expressions. The function invocation `f()` (for the OP's `f`) is an rvalue expression. And so is `f().a`. You can call member functions on rvalues. So can, for example, assign to `f()`. One apparent inconsistency arising from that is that if `f` has result type `struct X { int foo; };`, then you can assign to `f()` (the whole structure) due to the implicit assignment member function, but you can't assign to `f().foo`, because that would use the built-in assignment operator on an rvalue. :-) Cheers & hth., – Cheers and hth. - Alf Oct 30 '10 at 10:50
  • Also, `decltype(expr)` is different from `decltype((expr))`. The first yields the type of what is referenced in case that `expr` is an id-expression or a class member access. In this case, it's the latter, so it will yield `int[4]` *independent of rvalue or lvalueness*. In the case of `decltype((expr))`, if the type of the expression is `T`, it will yield `T&` for lvalues, and `T` for rvalues. So in our case, `decltyp((A().a))` is `int[4]` (not `int(&)[4]`). – Johannes Schaub - litb Oct 30 '10 at 11:04
  • I hate decltype and auto. They never seem to produce sensible results. – Puppy Oct 30 '10 at 11:08
  • 1
    Actually, my decltype explanation is still simplified as there are more special cases for `decltype` anyway :) – Johannes Schaub - litb Oct 30 '10 at 11:15