Binding rvalue to non-const reference via pointer cast?
Question
I don't understand how the following code compiles/doesn't compile:
struct Temp
{
int i;
};
int main(int argc, char * argv[])
{
//Temp &ref1 = (Temp){42}; // Error, as expected
Temp &ref2 = *(Temp*)&(Temp){42}; // A-OK
std::cerr << ref2.i << std::endl;
return 0;
}
I'm using g++ 4.4.4.
Solution
Your code is not really C++. It uses a compound literal which is a C99 feature. In C99 it evaluates to an lvalue and taking the address of the literal is completely fine there. Integrating this extension into C++, GCC appears to change the rule of it and make it an rvalue, better fitting the classification of them into the existing rules of C++ for usual casts that also produce rvalues.
GCC does not like &(Temp){42}
, complaining that I take the address of a temporary. It's a warning about invalid code which it still accepts but does not really like. The same warning is given for other obviously-wrong code like &A()
, which is a legal functional style C++ cast, which also produces an rvalue and thus cannot be used as the operand of the address-of operator.
The integration of compound literals into C++ by GCC also destroys the temporary prematurely, as can be seen by the following test
#include <iostream>
struct B {
~B() {
std::cout << "~B" << std::endl;
}
};
struct A { int i; B b; };
int main() {
A *a = &(A){0};
std::cout << "main" << std::endl;
}
In C99, the object that the literal refers to will be alive for the entire block (it will have automatic storage duration). In GNU C++ the object is destructed already at the end of the full expression, before the end of its block is even reached (the "~B" is printed before the "main").
OTHER TIPS
I could be mistaken about this, but this looks like a bug in the compiler. The C++ ISO standard, §5.3.1/2, discusses the &
operator as
The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id.
The section §5.4/1 discusses the casting operator as
The result of the expression (T) cast-expression is of type
T
. The result is an lvalue if T is a reference type, otherwise the result is an rvalue.
This seems to suggest that
(Temp){42}
Produces an rvalue, which you should not be legally allowed to take the address of using &
.
I've been known to make mistakes reading the spec before, so if someone could confirm this that would be awesome.
As templatetypedef has said, it does appear to be a bug in the compiler. When compiled with the 4.6.0 version of GCC, it gives:
error: taking address of temporary [-fpermissive]
And, of course, with the addition of -fpermissive, it compiles but complains, but still it doesn't crash and prints the correct result. I guess GCC is cheating a bit under "permissive" conditions.