Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Lifetime of temporary bound to aggregate initialized struct member

39 views
Skip to first unread message

Mat Noguchi

unread,
Apr 21, 2011, 5:01:54 PM4/21/11
to

(I originally asked this on stackoverflow (http://stackoverflow.com/q/
5719636/6210))

Given the following code:

class foo
{
};

class bar: public foo
{
public:
~bar() { printf("~bar()\n"); }
};

class zab: public foo
{
public:
~zab() { printf("~zab()\n"); }
};

struct foo_holder
{
const foo &f;
};

int main()
{
foo_holder holder[]= { {bar()}, {zab()} };
printf("done!\n");
return 0;
}

the output is:

~bar()
~zab()
done!

C++0x has a clause that dictates this can create dangling references
when used as a new initializer, but it says nothing (at least nothing
I can find) about aggregate initialization of const references with
temporaries.

Is this unspecified behavior?

MSN


--
[ comp.std.c++ is moderated. To submit articles, try posting with your ]
[ newsreader. If that fails, use mailto:std-cpp...@vandevoorde.com ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]

Daniel Krügler

unread,
Apr 23, 2011, 12:22:08 PM4/23/11
to

[Second attempt after ~24 h]

Am 21.04.2011 23:01, schrieb Mat Noguchi:
>
[..]


>
> Given the following code:
>
> class foo
> {
> };
>
> class bar: public foo
> {
> public:
> ~bar() { printf("~bar()\n"); }
> };
>
> class zab: public foo
> {
> public:
> ~zab() { printf("~zab()\n"); }
> };
>
> struct foo_holder
> {
> const foo&f;
> };
>
> int main()
> {
> foo_holder holder[]= { {bar()}, {zab()} };
> printf("done!\n");
> return 0;
> }
>
> the output is:
>
> ~bar()
> ~zab()
> done!
>
> C++0x has a clause that dictates this can create dangling references
> when used as a new initializer, but it says nothing (at least nothing
> I can find) about aggregate initialization of const references with
> temporaries.
>
> Is this unspecified behavior?

To me the current wording is clear and there are two errors in the
observed behaviour relative to the FDIS:

1) Both destructor calls should follow the output in main
2) The relative order of the destructor calls is incorrect

Thus, the expected order is specified to be

done!
~zab()
~bar()

Reasoning:

1) As of 12.2 [class.temporary] p. 5 this case is not among the
exceptions mentioned in the bulleted list which means we have
life-time extension here.

2) We have several normative wordings that make clear that the order
of initialization and destruction of the sub-elements is determined
here, e.g.:

a) 8.5.4 [dcl.init.list] p. 4:

"Within the initializer-list of a braced-init-list, the
initializer-clauses, including any that result from pack expansions
(14.5.3), are evaluated in the order in which they appear. That is,
every value computation and side effect associated with a given
initializer-clause is sequenced before every value computation and
side effect associated with any initializer-clause that follows it in
the comma-separated list of the initializer-list. [ Note: This
evaluation ordering holds regardless of the semantics of the
initialization; for example, it applies when the elements of the
initializer-list are interpreted as arguments of a constructor call,
even though ordinarily there are no sequencing constraints on the
arguments of a call. —end note ]"

which was added as result of resolving core issue 1030:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1030

b) 12.4 [class.dtor] p. 8:

"[..] Bases and members are destroyed in the reverse order of the
completion of their constructor (see 12.6.2). A return statement
(6.6.3) in a destructor might not directly return to the caller;
before transferring control to the caller, the destructors for the
members and bases are called. Destructors for elements of an array are
called in reverse order of their construction (see 12.6)."

Btw.: There exists a related bugentry for gcc in regard to this problem:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48370

so it seems that at least gcc is aware of this incorrect behaviour.

HTH & Greetings from Bremen,

Daniel Krügler

Saeed Amrollahi

unread,
Apr 23, 2011, 12:21:17 PM4/23/11
to

On Apr 22, 12:01 am, Mat Noguchi <msn.foo....@gmail.com> wrote:
> (I originally asked this on stackoverflow (http://stackoverflow.com/q/
> 5719636/6210))
>
> Given the following code:
>
> class foo
> {
> };
>
> class bar: public foo
> {
> public:
> ~bar() { printf("~bar()\n"); }
> };
>
> class zab: public foo
> {
> public:
> ~zab() { printf("~zab()\n"); }
> };
>
> struct foo_holder
> {
> const foo &f;
> };
>
> int main()
> {
> foo_holder holder[]= { {bar()}, {zab()} };
> printf("done!\n");
> return 0;
> }
>
> the output is:
>
> ~bar()
> ~zab()
> done!
>

About your output, I believe, it's wrong. It should be
~zab()
~bar()
done!
See N3291 section 12.4 paragraph 8:


Destructors for elements of an array are called in reverse order of

their construction.

About aggregate initialization, there is a similar example at section
12.2 paragraph 5.

HTH,
-- Saeed Amrollahi

> C++0x has a clause that dictates this can create dangling references
> when used as a new initializer, but it says nothing (at least nothing
> I can find) about aggregate initialization of const references with
> temporaries.
>
> Is this unspecified behavior?
>
> MSN
>
> --
> [ comp.std.c++ is moderated. To submit articles, try posting with your ]

> [ newsreader. If that fails, use mailto:std-cpp-sub...@vandevoorde.com ]

Daniel Krügler

unread,
Apr 23, 2011, 3:33:19 PM4/23/11
to

You are correct in regard the relative order of '~zab()' and '~bar()',
but the output of 'done!' should clearly happen before any of the
destructor calls, because we have life-time extension going on here.
This is clearly said in 12.2 p. 5 because this example is not among
the exceptions referred to in the bulleted list.

HTH & Greetings from Bremen,

Daniel Krügler


0 new messages