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.

Was it helpful?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top