8

In this question user Happy Mittal quotes section 12.2.5 of C++03 Standard: A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits.

How can that be useful anyway? I mean once the constructor exits the temporary gets destroyed, but the reference remains bound - now to an already destroyed object.

What's the point of so carefully specifying the temporary lifetime if there's still a dangling reference for the whole lifetime of the outer object? In which scenario can this behavior be useful?

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • 1
    I was asking myself the same question. Note that the wording is unchanged in the C++0x FCD (n3225), in 12.2 [class.temporary] note 5. – Matthieu M. Jan 18 '11 at 08:27

3 Answers3

8

It is not useful to have a reference member bound to a dead object, but it is useful to be clear that "normal" temporary lifetime extension when bound to a reference doesn't apply in this case.

It also specifies temporary lifetime extension that applies specially in the ctor initializer: it's extended to the end of the ctor rather than dying before the ctor body executes. This would not be useful except in "clever" classes whose whole point is executing the ctor, and this type of (ab)use is rightly avoided.

I know of no real world examples of the latter, but it strikes me akin to having destructors nothrow by default broke classes that were "clever" in their lifetime and how they were used. This did have real world uses and came up in discussions about how to handle the default semantics of dtors in C++0x.

Fred Nurk
  • 13,952
  • 4
  • 37
  • 63
2

In D language, construction process can be written freely in some degree. However in C++, construction/initialization order is strictly stipulated. So if class initialization requires some expensive computation, a code like the following sometimes may be valid as a reluctant workaround.

struct S {
  Args const &r;
  A a;
  B b;
  S( args.... )
    : r( expensive_func( args.... ) ), a( r.for_a ), b( r.for_b ) {}
};
Ise Wisteria
  • 11,259
  • 2
  • 43
  • 26
  • 1
    Refactored to avoid the reference member and resulting likely overhead in every instance: `struct SDetails { A a; B b; SDetails(Args const &r) : a (r.for_a), b (r.for_b) {} }; struct S : private SDetails { S(/*args...*/) : SDetails(expensive_func(/*args...*/)) {} };` – Fred Nurk Jan 18 '11 at 12:59
  • 1
    The refactoring gets even easier/better with C++0x: `struct S { A a; B b; S(/*args...*/) : S(expensive_func(/*args...*/)) {} private: S(Args const &r) : a (r.for_a), b (r.for_b) {} };` – Fred Nurk Jan 18 '11 at 13:01
  • So, in other words, though I recognize that initialization does sometimes need workarounds in C++03, I would never use a reference member this way. – Fred Nurk Jan 18 '11 at 13:03
  • @FredNurk: Thank you for helpful comments! Honestly, I'm not sure about the reason that the standard elongated the lifetime of temporaries generated in initializer list... – Ise Wisteria Jan 18 '11 at 13:45
1

It's useful for compiler writers. They already have logic in place to destroy bound temporaries at the end of a scope, and the exit of the constructor is one such point. With this rule compilers can reuse that point to destroy such temporaries as well.

Note that the standard really should decide on some lifetime, and the only other reasonable point would be after the ctor initializer list but before the ctor body. That's not a point where temporaries would be destroyed otherwise, and it could interfere with function-scope try {} catch() blocks (which do include the ctor initializer list)

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Isn't each initialization expression within the ctor initializer already a full-expression, since they are not the subexpression of another expression (C++03, §1.9p12)? If so, temporary object destruction is already required at those points. For example: `struct A { int foo, bar; A() : foo (T().m()), bar (T().m()) {} };` Compiler writers could just as easily re-use that (making compilers simpler overall) at the expense of the language allowing "always dead" reference members. – Fred Nurk Jan 18 '11 at 14:23
  • Well, that would have been possible too. But would that be useful? I'd suggest that the dangers of such a design outweigh its benefits. – MSalters Jan 18 '11 at 16:35
  • Isn't that the point? Reference members to lifetime-extended temporaries *aren't* useful, including as specified currently – except insofar as they specify *something* rather than leave it completely ambiguous, just like references bound to temporaries in a return statement. – Fred Nurk Jan 18 '11 at 17:46