The following scenario is given, to be interpreted as C++0x code:
struct B { };
struct A { B b; };
int main() {
B const& b = A().b;
/* is the object still alive here? */
}
Clang and GCC (trunk version as of 2011/02) behave differently: Clang lengthens the lifetime. GCC moves B
to a new temporary object, and then binds the reference to that new temporary.
I cannot find either behavior can be derived from the words of the Standard. The expression A().b
is not a temporary (see 5.2.5). Can anyone please explain the following to me?
Thanks!
T const * const aConstant (or const T * const aConstant ) declares a constant pointer to a constant T .
const T& value() const {... } This means the function value() will return a const T& type and in between (in the function) won't modify the class itself.
The benefit of const correctness is that it prevents you from inadvertently modifying something you didn't expect would be modified.
The first thing to know is that when you declare any variable or object, the qualifier 'const' can be written before or after the type.
In 12.2 paragraph 5 of N3126=10-0116 it's said that:
The second context [ in which temporaries are destroyed at a different point than the end of the full-expression ] is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except ...
and then follows a list of four special cases (ctor-inizializers, reference parameters, returned value, new initializer).
So (in this version) seems to me that clang is correct because you're binding the reference to a subobject of a temporary.
Thinking to the base sub-object of an object this also seems to be the only reasonable behavior. The alternative would mean doing a slicing in:
Derived foo();
...
void bar()
{
Base& x = foo(); // not very different from foo().b;
...
}
Actually after making a little experiment seems indeed that g++ differentiates between a member sub-object and a base sub-object, but I don't understand where this differentiation is made in the standard. The following is the test program I used and where it's clearly visible the different handling of the two cases... (B
is Base, D
is Derived and C
is composed).
#include <iostream>
struct B
{
B()
{ std::cout << "B{" << this << "}::B()\n"; }
B(const B& x)
{ std::cout << "B{" << this << "}::B(const B& " << &x << ")\n"; }
virtual ~B()
{ std::cout << "B{" << this << "}::~B()\n"; }
virtual void doit() const
{ std::cout << "B{" << this << "}::doit()\n"; }
};
struct D : B
{
D()
{ std::cout << "D{" << this << "}::D()\n"; }
D(const D& x)
{ std::cout << "D{" << this << "}::D(const D& " << &x << ")\n"; }
virtual ~D()
{ std::cout << "D{" << this << "}::~D()\n"; }
virtual void doit() const
{ std::cout << "D{" << this << "}::doit()\n"; }
};
struct C
{
B b;
C()
{ std::cout << "C{" << this << "}::C()\n"; }
C(const C& x)
{ std::cout << "C{" << this << "}::C(const C& " << &x << ")\n"; }
~C()
{ std::cout << "C{" << this << "}::~C()\n"; }
};
D foo()
{
return D();
}
void bar()
{
std::cout << "Before calling foo()\n";
const B& b = foo();
std::cout << "After calling foo()\n";
b.doit();
std::cout << "After calling b.doit()\n";
const B& b2 = C().b;
std::cout << "After binding to .b\n";
b2.doit();
std::cout << "After calling b2.doit()\n";
}
int main()
{
std::cout << "Before calling bar()\n";
bar();
std::cout << "After calling bar()\n";
return 0;
}
The output I get with g++ (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5 is
Before calling bar()
Before calling foo()
B{0xbf9f86ec}::B()
D{0xbf9f86ec}::D()
After calling foo()
D{0xbf9f86ec}::doit()
After calling b.doit()
B{0xbf9f86e8}::B()
C{0xbf9f86e8}::C()
B{0xbf9f86e4}::B(const B& 0xbf9f86e8)
C{0xbf9f86e8}::~C()
B{0xbf9f86e8}::~B()
After binding to .b
B{0xbf9f86e4}::doit()
After calling b2.doit()
B{0xbf9f86e4}::~B()
D{0xbf9f86ec}::~D()
B{0xbf9f86ec}::~B()
After calling bar()
In my opinion this is either a bug in g++ or a bug in what the c++ standard mandates if this is really the expected behavior or a possible acceptable behavior (but I must tell that I didn't really think about it a lot, this is just a feeling that something is wrong with this differentiation).
After refreshing my knowledge of the standard, I have to admit
that it is probably right to expect the object referred to by b
to remain alive (be extended) for the duration of scope in which the const& was initialized. I found GotW #88 a helpful source for this.
I fail to see how A().b
is structurally or semantically different from
string f() { return "abc"; } // ABC initializes return-value **TEMP**
void g() {
const string& s = f(); // initializes with reference to a temp
cout << s << endl; // '*&s' is extended per standard
}
Sorry for any confusion I might have caused. I was a little out of my depth there.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With