Topic: Why's of C++ -- (Constructors)


Author: "Greg Brewer" <nospam.greg@brewer.net>
Date: 1999/09/10
Raw View
Jerry Leichter <jerrold.leichter@smarts.com> wrote in message
news:37D6E47B.5AFD@smarts.com...
> There is no possible way to allow one constructor to call another
> without *some* extension to the separate compilation model.
> Consider:
>
> Foo::Foo() : y(0) { ... };
> Foo::Foo(int z) : Foo(), y(1) {...};
>
> Either we permit this code, or we don't.

I would disagree with the intent of that statement.  A general consensus
seems to be reached that your second statement should not be allowed.
Instead,

Foo::Foo() : y(0) {...};
Foo::Foo(int z) : Foo() {y=1;...}

would be required.  The main question has to do with what to do about
destructing the Foo object if an exception is thrown after Foo() has
completed but before Foo(int) has.  Personally, I think the dtor should be
called.  In support of this, suppose the Foo object has pointer objects and
the destructor deletes these objects if they exists.  If an exception is
thrown during the execution of Foo() then some of the pointers may not have
been initialized properly and it is impossible to accuractly test for
existance.  It can then be argued that once the Foo() is execution then all
objects should have been initialized.  If execution of Foo(int) then causes
an exeption, there is a reliable test for existance.

Greg Brewer
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Pierre Baillargeon <pb@artquest.net>
Date: 1999/09/09
Raw View
Lawrence Crowl wrote:

> In article <37CEED06.74622E86@artquest.net>,
> Pierre Baillargeon  <pb@artquest.net> wrote:
> >Consistently with my suggestion, once any constructor returns, the object is
> >considered constructed. The second constructor is simply doing more work. In
> >short, any constructor calling another can be considered as if you had called
> >a function just after the first constructor call.
>
> The semantics cannot be quite the same.  The current language has the
> constraint that from the original caller's standpoint, an exception
> thrown from a constructor must mean the object is not constructed.
> Your semantics violates that constraint.

No, the object destructor will be called if an exception is thrown, so from the
caller point of view, nothing has changed. My wording may have given you the
impression that I meant that the second constructor is no longer considered a
constructor as far as automatically calling the destructor upon an exception. My
idea was not that. That is what meant to be conveyed by the usage of "In short" and
"can be considered as if".


> However, this semantic interpretation requires knowledge of the
> implementation of the called constructor.  We do not want to build the
> language definition around cutting and pasting source code when the
> compilers won't in general have access to that code.

What semantic interpretation? The generated code for the call to the other
constructor is in the new-type constructors, not in the client code. Maybe my
illustration gave you the impression that the call to the "real" constructor was
placed in client code.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Jerry Leichter <jerrold.leichter@smarts.com>
Date: 1999/09/09
Raw View
| >Let me suggest a simple semantics for the occurence of a constructor
| >within a constructor initialization list:  [Merging initialization
| >lists and constructor bodies.]
| Unfortunately, this semantics relies on the compilers having knowledge
| of the implementation of other constructors when compiling a
| constructor.  We don't want to require that because it would break
| separate compilation.

There is no possible way to allow one constructor to call another
without *some* extension to the separate compilation model.
Consider:

 Foo::Foo() : y(0) { ... };
 Foo::Foo(int z) : Foo(), y(1) {...};

Either we permit this code, or we don't.

If we permit it, and we want true separate compilation in the
traditional sense, then there is no way to avoid having y initialized
twice.  But there is no possible C++ semantics for that that makes much
sense.  You can't initialize y a second time without first destructing
it, and constructing and then destructing y is not necessarily a no-op
with respect to program state.  Further, C++ has guarantees both about
the order in which the constructors for class members are invoked (in
order of declaration), and about the order in which destructors run (in
inverse order).  Other constructors may have run *after* y's; we can't
run y's destructor without first run *their* destructors:  For one
thing, they have legitimately depend on the already-constructed y.

Conversely, to forbid this construction in a way, we really would like
some way for a compiler to check for a violation.  (Yes, we could add
yet another way for programmers to hang themselves with undiagnosed
errors - but do we *really* want that?)  Even that's impossible in the
traditional separate compilation model.

What you need either way is exactly the same kind of information needed
to implement the "merge" model I suggested.  However, it's also possible
to implement the "merge" model in other ways - e.g., for each
constructor, build it as three routines:  One for the initializers, one
for the body, and a third that calls the first two in order.  The
"re-written" merged code would then actually call the appropriate
functions.  (If you want to trade time for space, the third can contain
the code for the first two, rather than calling them.  If you do this, a
reasonably smart linker might omit the first and second if they are
never called.)

What it comes down to is:  The separate compilation model is simply to
weak to be used here, unless you are willing to live very dangerously.
Then again, the tradional separate compilation model is unreasonable for
templates anyway.  Linkers have been forced to become smarter.  It's not
unreasonable to suggest that they could learn how to deal with more
advanced constructor semantics.
       -- Jerry
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Lawrence.Crowl@eng.sun.com (Lawrence Crowl)
Date: 1999/09/04
Raw View
In article <37CEED06.74622E86@artquest.net>,
Pierre Baillargeon  <pb@artquest.net> wrote:
>
>Michael Entin wrote:
>
>> I also do not know what to expect from the following code
>>
>> class Foo
>> {
>>   SomeClass m_a;
>> public:
>>   Foo ()
>>     : m_a (0)
>>     {
>>     }
>>
>>   Foo::Foo( int a )
>>     : m_a (1), Foo()
>>     {
>>     }
>> }
>
>Simply require that any constructor call must be the first, and any further
>initialization is considered an assignment. So the second constructor becomes
>a "normal" member function in respect to initialization.
>
...
>
>Consistently with my suggestion, once any constructor returns, the object is
>considered constructed. The second constructor is simply doing more work. In
>short, any constructor calling another can be considered as if you had called
>a function just after the first constructor call.

The semantics cannot be quite the same.  The current language has the
constraint that from the original caller's standpoint, an exception
thrown from a constructor must mean the object is not constructed.
Your semantics violates that constraint.

>
>So the following imaginary code:
>
>    class Foo
>    {
>      SomeClass m_a;
>    public:
>      Foo ()
>        : m_a (0)
>        {
>        }
>
>      Foo::Foo( int a )
>        : Foo(), m_a (1)
>        {
>        }
>    }
>
>Is equivalent to the following valid code:
>
>    class Foo
>    {
>      SomeClass m_a;
>    public:
>      Foo ()
>        : m_a (0)
>        {
>        }
>
>      FooFoo( int a )    // MUST BE CALLED AFTER CONSTRUCTOR!
>        {
>           m_a = 1;
>        }
>    }
>
>But is safer as the intention is not enforced by a simple comment.

However, this semantic interpretation requires knowledge of the
implementation of the called constructor.  We do not want to build the
language definition around cutting and pasting source code when the
compilers won't in general have access to that code.


  Lawrence Crowl                650-786-6146   Sun Microsystems, Inc.
                  Lawrence.Crowl@Eng.Sun.Com   901 San Antonio Road, UMPK16-303
                                               Palo Alto, California, 94303

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Lawrence.Crowl@eng.sun.com (Lawrence Crowl)
Date: 1999/09/04
Raw View
In article <7qle59$919$1@storm.comstar.ru>, Michael Entin <entin@sw.ru> wrote:
>
>I also do not know what to expect from the following code
>
>class Foo
>{
>  SomeClass m_a;
>public:
>  Foo ()
>    : m_a (0)
>    {
>    }
>
>  Foo::Foo( int a )
>    : m_a (1), Foo()
>    {
>    }
>}
>
>Should m_a be initialized with 1 or 0 ?

The code shouldn't be allowed.  Without full knowledge of the
implementation of Foo(), Foo( int ) could be inconsistent with Foo()
with respect to m_a.  And we do want to allow separate compilation
(limited knowledge).

  Lawrence Crowl                650-786-6146   Sun Microsystems, Inc.
                  Lawrence.Crowl@Eng.Sun.Com   901 San Antonio Road, UMPK16-303
                                               Palo Alto, California, 94303


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Lawrence.Crowl@eng.sun.com (Lawrence Crowl)
Date: 1999/09/04
Raw View
In article <37D0221A.FA6@smarts.com>,
Jerry Leichter  <jerrold.leichter@smarts.com> wrote:
>
>Let me suggest a simple semantics for the occurence of a constructor
>within a constructor initialization list:  If we have (Greek letter
>names correspond to arbitary argument lists/initialization lists/
>expressions/etc.):
>
> Foo::Foo(alpha)
>  : Foo(beta-actuals), alpha-init
> { alpha-body }
>
> Foo::Foo(beta)
>  : beta-expr
> { beta-body }
>
>then we re-write this to:
>
> Foo::Foo(alpha)
>  : alpha-init, beta-init[beta-actuals]
> { beta-body[beta-actuals];
>  alpha-body;
> }
>
>where, e.g., beta-init[beta-actuals] is the Foo(beta) initializers with
>values substituted from the Foo(beta-actuals) expression.

Unfortunately, this semantics relies on the compilers having knowledge
of the implementation of other constructors when compiling a
constructor.  We don't want to require that because it would break
separate compilation.

  Lawrence Crowl                650-786-6146   Sun Microsystems, Inc.
                  Lawrence.Crowl@Eng.Sun.Com   901 San Antonio Road, UMPK16-303
                                               Palo Alto, California, 94303

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: scorp@btinternet.com (Dave Harris)
Date: 1999/09/04
Raw View
jerrold.leichter@smarts.com (Jerry Leichter) wrote:
>  Foo::Foo(alpha)
>   : alpha-init, beta-init[beta-actuals]
>  { beta-body[beta-actuals];
>   alpha-body;
>  }
> [...]
> Since Foo(beta)'s initializers are merged in with Foo(alpha)'s, there
> is no meaningful sense in which "Foo(beta) has finished
> initializing even though Foo(alpha) hasn't, except perhaps
> after beta-body has executed.

Although this answers the question, I don't like it much for two reasons.
The first is the point other people have raised, about how to make it
efficient when the two constructors are in separate compilation units.
Only the linker has enough information to interleave the alpha and beta
parts. To make it work with a dumb linker, we'd have to split the beta
constructor into at least 2 separate functions, one for beta_init and one
for beta-body. This is only needed if alpha is implemented in terms of
beta, but we don't know about that when compiling beta, so we must do it
always. So we end up paying a cost even when the feature isn't used, at
least when using current linkers.

Secondly, I am bothered by your "except perhaps" clause. Usually when we
get to the end of beta-body, the object is truly constructed and we can
rely on its destructor being called. Beta may be written with that in
mind. With your proposal, that guarantee is lost. Now when beta completes,
we may or may not be fully constructed and the destructor may or may not
be called. I suppose this uncertainty isn't fatal, but I don't like it
much.

  Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
      brangdon@cix.co.uk      |   And close your eyes with holy dread,
                              |  For he on honey dew hath fed
 http://www.bhresearch.co.uk/ |   And drunk the milk of Paradise."


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: scorp@btinternet.com (Dave Harris)
Date: 1999/09/02
Raw View
entin@sw.ru (Michael Entin) wrote:
> I also do not know what to expect from the following code
>
> class Foo
> {
>   SomeClass m_a;
> public:
>   Foo ()
>     : m_a (0)
>     {
>     }
>
>   Foo::Foo( int a )
>     : m_a (1), Foo()
>     {
>     }
> }
>
> Should m_a be initialized with 1 or 0 ?

Would anything be lost by forbidding such code? If a constructor calls
another constructor, it can't do any other initialisation. (It can do
other work inside the braces, of course.)

In practice it could be written as:
    Foo::Foo( int a, int b ) : m_a(b) {
    }
    Foo::Foo( int a ) : Foo( a, 1 ) {
    }
    Foo::Foo() : Foo( 0, 0 ) {
    }

You have one constructor that does all the work, and the others just call
it. If the work constructor must take extra arguments, or be declared
"private", so be it.

  Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
      brangdon@cix.co.uk      |   And close your eyes with holy dread,
                              |  For he on honey dew hath fed
 http://www.bhresearch.co.uk/ |   And drunk the milk of Paradise."


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: scorp@btinternet.com (Dave Harris)
Date: 1999/09/02
Raw View
lisa_lippincott@advisories.com (Lisa Lippincott) wrote:
> Foo::Foo( int a, bool b )
>   : Foo( a )
>   {
>    if ( b )
>       throw "Am I constructed?";
>   }
>
> When this constructor throws, is the destructor for Foo called?

Good question, but surely the answer would be, "Yes, of course". When
Foo(a) is complete, the object is fully constructed.

  Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
      brangdon@cix.co.uk      |   And close your eyes with holy dread,
                              |  For he on honey dew hath fed
 http://www.bhresearch.co.uk/ |   And drunk the milk of Paradise."


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: juliano@cs.unc.edu (Jeffrey Juliano)
Date: 1999/09/03
Raw View
On 2 Sep 1999 15:41:20 GMT, blargg <postmast.root.admi.gov@iname.com> wrote:
>
[...]
>
>Just to review, there is also the issue of who can have member
>initializers. Is the "primary" constructor allowed to have member
>initializers, or are they disallowed when a secondary constructor is
>called? This would likely be an important limitation in many cases, but a
>solution to it would have to be fairly complex. How would double

[... good points that would have to be addressed ...]

It would be even more complex if the two constructors are in different
translation units.  Additional link-time support would be necessary.
Initializers aren't specified until a member function is defined.

-jeff
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Greg Brewer" <nospam.greg@brewer.net>
Date: 1999/09/03
Raw View
Lisa Lippincott <lisa_lippincott@advisories.com> wrote in message
news:010919991833259458%lisa_lippincott@advisories.com...
> I assume you mean some construct like this:
>
> Foo::Foo( int a, bool b )
>   : Foo( a )
>   {
>    if ( b )
>       throw "Am I constructed?";
>   }
>
> When this constructor throws, is the destructor for Foo called?

That would have to be defined.  I would not disagree that some refinements
are necessary.

Greg Brewer
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Lisa Lippincott <lisa_lippincott@advisories.com>
Date: 1999/09/03
Raw View
I asked:
> Foo::Foo( int a, bool b )
>   : Foo( a )
>   {
>    if ( b )
>       throw "Am I constructed?";
>   }
>
> When this constructor throws, is the destructor for Foo called?

Dave Harris <scorp@btinternet.com> answered:

> Good question, but surely the answer would be, "Yes, of course". When
> Foo(a) is complete, the object is fully constructed.

I'm inclined to agree.  Now for extra credit, what should this
constructor do?

Foo::Foo( int a, bool b )
  try
     : Foo( a )
     {
      if ( b )
         throw "Am I constructed?";
     }
    catch( ... )
     {
     }

                                                --Lisa Lippincott
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Pierre Baillargeon <pb@artquest.net>
Date: 1999/09/03
Raw View
This is a multi-part message in MIME format.
--------------D70732A8DAE2CB6DC1D5093E
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Michael Entin wrote:

> I also do not know what to expect from the following code
>
> class Foo
> {
>   SomeClass m_a;
> public:
>   Foo ()
>     : m_a (0)
>     {
>     }
>
>   Foo::Foo( int a )
>     : m_a (1), Foo()
>     {
>     }
> }

Simply require that any constructor call must be the first, and any further
initialization is considered an assignment. So the second constructor becomes
a "normal" member function in respect to initialization.

> "Lisa Lippincott" <lisa_lippincott@advisories.com> wrote in message
> news:010919991833259458%lisa_lippincott@advisories.com...
> | I assume you mean some construct like this:
> |
> | Foo::Foo( int a, bool b )
> |   : Foo( a )
> |   {
> |    if ( b )
> |       throw "Am I constructed?";
> |   }
> |
> | When this constructor throws, is the destructor for Foo called?
> |

Consistently with my suggestion, once any constructor returns, the object is
considered constructed. The second constructor is simply doing more work. In
short, any constructor calling another can be considered as if you had called
a function just after the first constructor call.

So the following imaginary code:

    class Foo
    {
      SomeClass m_a;
    public:
      Foo ()
        : m_a (0)
        {
        }

      Foo::Foo( int a )
        : Foo(), m_a (1)
        {
        }
    }

Is equivalent to the following valid code:

    class Foo
    {
      SomeClass m_a;
    public:
      Foo ()
        : m_a (0)
        {
        }

      FooFoo( int a )    // MUST BE CALLED AFTER CONSTRUCTOR!
        {
           m_a = 1;
        }
    }

But is safer as the intention is not enforced by a simple comment.


--------------D70732A8DAE2CB6DC1D5093E
Content-Type: text/x-vcard; charset=us-ascii;
 name="pb.vcf"
Content-Description: Card for Pierre Baillargeon
Content-Disposition: attachment;
 filename="pb.vcf"
Content-Transfer-Encoding: quoted-printable
X-MIME-Autoconverted: from 8bit to quoted-printable by ncar.UCAR.EDU id PAA02694

begin:vcard=20
n:Baillargeon;Pierre
tel;fax:(450) 976-5907
tel;work:(450) 976-7878
x-mozilla-html:TRUE
url:www.artquest.net
org:Alliance ArtQuest International Inc.;Software
version:2.1
email;internet:pb@artquest.net
title:Software Engineer
adr;quoted-printable:;;666 boul. St.-Martin Ouest=3D0D=3D0Abureau 202;Lav=
al;Qu=E9bec;H7M 5G4;Canada
fn:Pierre Baillargeon
end:vcard

--------------D70732A8DAE2CB6DC1D5093E--
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: postmast.root.admi.gov@iname.com (blargg)
Date: 1999/09/03
Raw View
In article <slrn7statt.pqs.juliano@capefear.cs.unc.edu>,
juliano@cs.unc.edu (Jeffrey Juliano) wrote:

> On 2 Sep 1999 15:41:20 GMT, blargg <postmast.root.admi.gov@iname.com> wrote:
> >
> [...]
> >
> >Just to review, there is also the issue of who can have member
> >initializers. Is the "primary" constructor allowed to have member
> >initializers, or are they disallowed when a secondary constructor is
> >called? This would likely be an important limitation in many cases, but a
> >solution to it would have to be fairly complex. How would double
>
> [... good points that would have to be addressed ...]
>
> It would be even more complex if the two constructors are in different
> translation units.

It would have also been more interesting if you had quoted (and read)
where I said exactly that in the message above:

>> Consider different sets of definitions for the two Y constructors (where
>> the two constructor definitions aren't necessarily in the same translation
>> unit):

:-)

> Additional link-time support would be necessary.
> Initializers aren't specified until a member function is defined.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: postmast.root.admi.gov@iname.com (blargg)
Date: 1999/09/03
Raw View
In article <37CEED06.74622E86@artquest.net>, Pierre Baillargeon
<pb@artquest.net> wrote:

> This is a multi-part message in MIME format.
> --------------D70732A8DAE2CB6DC1D5093E
> Content-Type: text/plain; charset=us-ascii
> Content-Transfer-Encoding: 7bit
>
> Michael Entin wrote:
>
> > I also do not know what to expect from the following code
> >
> > class Foo
> > {
> >   SomeClass m_a;
> > public:
> >   Foo ()
> >     : m_a (0)
> >     {
> >     }
> >
> >   Foo::Foo( int a )
> >     : m_a (1), Foo()
> >     {
> >     }
> > }
>
> Simply require that any constructor call must be the first, and any further
> initialization is considered an assignment.

Oh, that's nice. Change the meaning of a member INITIALIZER. No thanks. If
you aren't going to allow member initializers to do what they're supposed
to do, don't allow them at all. Require the user to do the assignments
themselves in the body.

> So the second constructor becomes
> a "normal" member function in respect to initialization.

Yep.

[snip]
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Lisa Lippincott <lisa_lippincott@advisories.com>
Date: 1999/09/02
Raw View
Greg Brewer <nospam.greg@brewer.net> sums up, dismissing many cogent
replies as "no valid reason."  But I don't remember seeing this topic
addressed:

> No valid reason was presented as to why a data constructor could not
> call other, overloaded constructors of the same class.

I assume you mean some construct like this:

Foo::Foo( int a, bool b )
  : Foo( a )
  {
   if ( b )
      throw "Am I constructed?";
  }

When this constructor throws, is the destructor for Foo called?

                                                   --Lisa Lippincott
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Michael Entin" <entin@sw.ru>
Date: 1999/09/02
Raw View
I also do not know what to expect from the following code

class Foo
{
  SomeClass m_a;
public:
  Foo ()
    : m_a (0)
    {
    }

  Foo::Foo( int a )
    : m_a (1), Foo()
    {
    }
}

Should m_a be initialized with 1 or 0 ?
In this example, should m_a constructor be called twice (and
destructor once) or it should be called just once (any ideas
how to implement it without having extra flag that prevents
it from being initialized twice ?)

This type of things is good in Java, where constructor is
simple function that has no specail behaviour (no VPTR
adjastments, no member initialization). In C++ it can
hardly be implemented and be well-defined.

Mike.
--
entin@bigfoot.com, http://chat.ru/~entin

"Lisa Lippincott" <lisa_lippincott@advisories.com> wrote in message
news:010919991833259458%lisa_lippincott@advisories.com...
| Greg Brewer <nospam.greg@brewer.net> sums up, dismissing many cogent
| replies as "no valid reason."  But I don't remember seeing this topic
| addressed:
|
| > No valid reason was presented as to why a data constructor could not
| > call other, overloaded constructors of the same class.
|
| I assume you mean some construct like this:
|
| Foo::Foo( int a, bool b )
|   : Foo( a )
|   {
|    if ( b )
|       throw "Am I constructed?";
|   }
|
| When this constructor throws, is the destructor for Foo called?
|
|                                                    --Lisa Lippincott




[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: postmast.root.admi.gov@iname.com (blargg)
Date: 1999/09/02
Raw View
In article <010919991833259458%lisa_lippincott@advisories.com>, Lisa
Lippincott <lisa_lippincott@advisories.com> wrote:

> Greg Brewer <nospam.greg@brewer.net> sums up, dismissing many cogent
> replies as "no valid reason."

hehe

> But I don't remember seeing this topic addressed:
>
> > No valid reason was presented as to why a data constructor could not
> > call other, overloaded constructors of the same class.
>
> I assume you mean some construct like this:
>
> Foo::Foo( int a, bool b )
>   : Foo( a )
>   {
>    if ( b )
>       throw "Am I constructed?";
>   }
>
> When this constructor throws, is the destructor for Foo called?

Hmmm, I had a quick answer, but now I see your point. I am silent. Good
catch. I mean throw. Whatever... :-)

Just to review, there is also the issue of who can have member
initializers. Is the "primary" constructor allowed to have member
initializers, or are they disallowed when a secondary constructor is
called? This would likely be an important limitation in many cases, but a
solution to it would have to be fairly complex. How would double
construction of members be prevented in the case that the primary and
secondary constructors both provided an initializer? Would one be ignored?
This is clearly bad. How would the compiler prevent this situation,
without necessarily having the definitions of both constructors at hand?
Even worse, what about member initialization order?

    class X {
    public:
        X( int );
    };

    class Y {
        X a, b, c;
    public:
        Y();
        Y( int );
    };

Consider different sets of definitions for the two Y constructors (where
the two constructor definitions aren't necessarily in the same translation
unit):

    Y::Y( int i ) : a( i ), b( i + 1 ), c( i + 2 ) { }

    Y::Y() : Y( 0 ) { }

OK.


    Y::Y( int i ) : a( i ), b( i + 1 ) { }

    Y::Y() : Y( 0 ), c( 10 ) { }

How does the compiler check this?


    Y::Y( int i ) : a( i ), b( i + 1 ) { }

    Y::Y() : Y( 0 ), a( 10 ) { }

How does the compiler check this?


    Y::Y( int i ) : b( i ), c( i + 1 ) { }

    Y::Y() : Y( 0 ), a( 10 ) { }

Is Y( int ) called before or after initializing a? It would have to be
after to preserve correct member initializer order, but how does the
compiler know this?


The solution of having an extra class is looking simpler all the time.

Sometimes I use a deferred-construction helper object (like freestore, but
allocates space for the object inside itself) to allow a normal common
initialization function to be used.


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: postmast.root.admi.gov@iname.com (blargg)
Date: 1999/09/03
Raw View
In article <memo.19990902204009.58487B@btinternet.com>, brangdon@cix.co.uk
wrote:

> entin@sw.ru (Michael Entin) wrote:
> > I also do not know what to expect from the following code
> >
> > class Foo
> > {
> >   SomeClass m_a;
> > public:
> >   Foo ()
> >     : m_a (0)
> >     {
> >     }
> >
> >   Foo::Foo( int a )
> >     : m_a (1), Foo()
> >     {
> >     }
> > }
> >
> > Should m_a be initialized with 1 or 0 ?
>
> Would anything be lost by forbidding such code?

Perhaps. What if one wanted to initialize some members in the common
constructor, and others in the base constructor.

I suppose this restriction could be a fallback if no solution could be
found to allowing member initializers in the top-level constructor.

> If a constructor calls
> another constructor, it can't do any other initialisation.

I assume you're just reiterating what you said above. In case you aren't,
since this is not allowed in the language at all (calling another
constructor like this), we can't say "well, it can't do that" since we are
writing the rules of what it can and can't do.

> (It can do other work inside the braces, of course.)
>
> In practice it could be written as:
>
>     Foo::Foo( int a, int b ) : m_a(b) {
>     }
>
>     Foo::Foo( int a ) : Foo( a, 1 ) {
>     }
>
>     Foo::Foo() : Foo( 0, 0 ) {
>     }
>
> You have one constructor that does all the work, and the others just call
> it. If the work constructor must take extra arguments, or be declared
> "private", so be it.

I guess this could work. What more could you do in a top-level member
initializer that couldn't be done in a member initializer in the nested
ctor call?

It would be useful to get some practical data on this: Find out how many
cases nested ctor calls would be useful in current code (especially where
it is emulated with some idiom), and how many benefit from this restricted
version (or could be easily made to benefit from it without any
significant semantic changes). I'll check through my library code and post
some results. I have a feeling you're right, and that this restriction
wouldn't be too restrictive in practice. Plus one could always fall back
on having an extra class with one constructor that is used to factor out
the common code.

Just today I was writing a class where a common constructor would have
been useful.

"I think we may have something"

This restriction combined with the notion that construction is complete
after execution of the nested constructor could be a useful version of
this idea. Both of these issues are still debatable, though.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Jerry Leichter <jerrold.leichter@smarts.com>
Date: 1999/09/03
Raw View
| Foo::Foo( int a, bool b )
|   try
|      : Foo( a )
|      {
|       if ( b )
|          throw "Am I constructed?";
|      }
|     catch( ... )
|      {
|      }

Let me suggest a simple semantics for the occurence of a constructor
within a constructor initialization list:  If we have (Greek letter
names correspond to arbitary argument lists/initialization lists/
expressions/etc.):

 Foo::Foo(alpha)
  : Foo(beta-actuals), alpha-init
 { alpha-body }

 Foo::Foo(beta)
  : beta-expr
 { beta-body }

then we re-write this to:

 Foo::Foo(alpha)
  : alpha-init, beta-init[beta-actuals]
 { beta-body[beta-actuals];
  alpha-body;
 }

where, e.g., beta-init[beta-actuals] is the Foo(beta) initializers with
values substituted from the Foo(beta-actuals) expression.

This answers all questions about order of execution, repeated
initializations (illegal), when ~Foo should be run (though the answer it
gives is *not* quite the one previously suggested:  Since Foo(beta)'s
initializers are merged in with Foo(alpha)'s, there is no meaningful
sense in which "Foo(beta) has finished initializing even though
Foo(alpha) hasn't, except perhaps after beta-body has executed.), etc.
It doesn't answer your try question, which is a rather subtle one.  In
the example as you have it, there's only one try/catch block, so it's
actually not that big a deal:  We can still substitute Foo's value in
and go on from there.  But what if *both* initializers have try/catch
clauses?  You want some kind of nested try/catch block semantics, but
it's not something you can write in C++.  (You want exceptions thrown by
beta-init or beta-body to be caught first by beta's try/catch, then by
alpha's; but exceptions from alpha-init and alpha-body should be caught
directly by alpha's try/catch.  Writing this out for the -body's is easy
if verbose; but there's no way to write the required code for beta-init.

Of course, it's not necessary that *all* the semantics be given by a
C++-to-C++ rewrite rule.
       -- Jerry


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]