markr@and.cs.liv.ac.uk (04/02/91)
Just a simple question: Can I call one constructor to class X explicitly from within another constructor to class X ? Example: constructor X::X( int i ) performs a lot of initialisation that needs to be done at the start of X::X( char *c ) too. Can I write: X::X( char *c ) { X( 5 ); // rest of stuff ? Or maybe something similar with different syntax? Apologies for turning the group into a tutorial session, but I can't find any reference to this in my C++ books. Mark Rivers markr@uk.ac.liverpool.compsci , via an appropriate relay if necessary
fuchs@t500e0.telematik.informatik.uni-karlsruhe.de (Harald Fuchs) (04/04/91)
markr@and.cs.liv.ac.uk writes: >Can I call one constructor to class X explicitly from within another >constructor to class X ? >Example: constructor X::X( int i ) performs a lot of initialisation that needs >to be done at the start of X::X( char *c ) too. Can I write: >X::X( char *c ) { > X( 5 ); > // rest of stuff >? Or maybe something similar with different syntax? >Apologies for turning the group into a tutorial session, but I can't find any >reference to this in my C++ books. That's because there is no such thing. When I have to do lots of common initialization, I put it into a private member function (e.g. void init ();) and call this function from the constructors. Unfortunately, this won't work if you have to initialize constant or reference data members. IMHO a hole in the language. -- Harald Fuchs <fuchs@telematik.informatik.uni-karlsruhe.de>
gwu@nujoizey.tcs.com (George Wu) (04/04/91)
In article <1991Apr2.110623.22219@and.cs.liv.ac.uk>, markr@and.cs.liv.ac.uk writes: |> Can I call one constructor to class X explicitly from within another |> constructor to class X ? |> |> Example: constructor X::X( int i ) performs a lot of initialisation that needs |> to be done at the start of X::X( char *c ) too. Can I write: |> |> X::X( char *c ) { |> X( 5 ); |> // rest of stuff What you wish to do is commonly accomplished by calling a third method containing the common initlization code. For example: X::init() { // initialization of code common to all constructors goes here } X::X(int i) { init(); // . . . } X::X(char *s) { init(); // . . . } ---- George J Wu, Software Engineer | gwu@tcs.com or uunet!tcs!gwu Teknekron Communications Systems, Inc.| (415) 649-3752 2121 Allston Way, Berkeley, CA, 94704 | Quit reading news. Get back to work.
steve@taumet.com (Stephen Clamage) (04/05/91)
fuchs@t500e0.telematik.informatik.uni-karlsruhe.de (Harald Fuchs) writes: >When I have to do lots of >common initialization, I put it into a private member function >(e.g. void init ();) and call this function from the constructors. >Unfortunately, this won't work if you have to initialize constant or >reference data members. IMHO a hole in the language. You put these in the constructor header, not the body, and hence not in an init function. Such items may be not be assigned to, but only initialized. I don't necessarily see this as a hole in the language, but does require some repetition in multiple constructors. -- Steve Clamage, TauMetric Corp, steve@taumet.com
ark@alice.att.com (Andrew Koenig) (04/05/91)
In article <1991Apr2.110623.22219@and.cs.liv.ac.uk> markr@and.cs.liv.ac.uk writes: > Can I call one constructor to class X explicitly from within another > constructor to class X ? No. > Example: constructor X::X( int i ) performs a lot of initialisation that needs > to be done at the start of X::X( char *c ) too. Can I write: > X::X( char *c ) { > X( 5 ); > // rest of stuff Do it this way: class X { private: common_stuff() { /* ... */ } public: X(int i) { common_stuff(); /* other stuff */ } X(char* c) { common_stuff(); /* other stuff */ } // and so on }; -- --Andrew Koenig ark@europa.att.com
horstman@mathcs.sjsu.edu (Cay Horstmann) (04/05/91)
In article <fuchs.670695206@t500e0> fuchs@t500e0.telematik.informatik.uni-karlsruhe.de (Harald Fuchs) writes: >markr@and.cs.liv.ac.uk writes: > >>Can I call one constructor to class X explicitly from within another >>constructor to class X ? > >>Example: constructor X::X( int i ) performs a lot of initialisation that needs >>to be done at the start of X::X( char *c ) too. Can I write: > >>X::X( char *c ) { >> X( 5 ); >> // rest of stuff > > >>? Or maybe something similar with different syntax? > >>Apologies for turning the group into a tutorial session, but I can't find any >>reference to this in my C++ books. > >That's because there is no such thing. That isn't quite true. One could presumably call the constructor explicitly with the placement syntax: new(this) X(5); I am NOT going to advocate this style. In fact, it seems the totally WRONG thing to do since it can be brokem by redefining X::operator new( size_t, void* ). >When I have to do lots of >common initialization, I put it into a private member function >(e.g. void init ();) and call this function from the constructors. >Unfortunately, this won't work if you have to initialize constant or >reference data members. IMHO a hole in the language. > Good point! Cay
rae@alias.com (Reid Ellis) (04/07/91)
Harald Fuchs <fuchs@t500e0.telematik.informatik.uni-karlsruhe.de> writes: >When I have to do lots of >common initialization, I put it into a private member function >(e.g. void init ();) and call this function from the constructors. >Unfortunately, this won't work if you have to initialize constant or >reference data members. IMHO a hole in the language. Not really a hole. As pointed out, you have to do some repetition sometimes. But even this can be cut down, although in a way that I think violates the spirit of initializers: ---- #include <stream.h> struct X { X(); int init_all(); int a; char str[80]; float f; int &aRef; }; X::X() : a(init_all()), aRef(a) { } int X::init_all() { static int count = 1; sprintf(str, "This is test #%d", count++); f = 1.23; return 23; } int main() { X anX; const X constX; cout << form(" anX = {%d, \"%s\", %g, %d}\n", anX.a, anX.str, anX.f, anX.aRef); cout << form("constX = {%d, \"%s\", %g, %d}\n", constX.a, constX.str, constX.f, constX.aRef); return 0; } ---- When run, you should get something like: anX = {23, "This is test #1", 1.23, 23} constX = {23, "This is test #2", 1.23, 23} Note that a) this works even for const objects, unlike the generic init() b) you still have to initialize references normally Reid -- Reid Ellis 1 Trefan Street Apt. E, Toronto ON, M5A 3A9 rae@utcs.toronto.edu || rae%alias@csri.toronto.edu CDA0610@applelink.apple.com || +1 416 362 9181 [work]
francis@sunquest.UUCP (Francis Sullivan) (04/12/91)
ark@alice.att.com (Andrew Koenig) writes: >In article <1991Apr2.110623.22219@and.cs.liv.ac.uk> markr@and.cs.liv.ac.uk writes: >> Can I call one constructor to class X explicitly from within another >> constructor to class X ? >No. Yes, by using operator= #include <stdio.h> class X { int j; public: X(int i) : j(i) { printf("Called X(int=%d): j=%d\n", i, j); } X(char *cp, int i) { *this = X(i); printf("Called X(char *cp= '%s', int=%d): j=%d\n", cp, i, j); } }; main() { X x("test", 5); } $ main Called X(int=5): j=5 Called X(char *cp= 'test', int=5): j=5 -- Francis Sullivan Sunquest Information Systems email: francis@sunquest.com or 930 N. Finance Center Drive {uunet,arizona}!sunquest!francis Tucson, AZ 85710 (602)885-7700
ark@alice.att.com (Andrew Koenig) (04/12/91)
In article <17400@sunquest.UUCP> francis@sunquest.UUCP (Francis Sullivan) writes: > ark@alice.att.com (Andrew Koenig) writes: > >In article <1991Apr2.110623.22219@and.cs.liv.ac.uk> markr@and.cs.liv.ac.uk writes: > >> Can I call one constructor to class X explicitly from within another > >> constructor to class X ? > >No. > Yes, by using operator= Sorry, but still no. The usage you propose is implicitly (not explicitly) calling the constructor to construct a *different* object. The original question was whether it was possible for one constructor of a class to call another constructor to help it construct the same object; the answer to that question is `no.' -- --Andrew Koenig ark@europa.att.com
hitz@sim5.csi.uottawa.ca (Martin Hitz) (04/12/91)
In article <17400@sunquest.UUCP> francis@sunquest.UUCP (Francis Sullivan) writes: >>> Can I call one constructor to class X explicitly from within another >>> constructor to class X ? > >Yes, by using operator= > >#include <stdio.h> > >class X { > int j; > >public: > X(int i) : j(i) { printf("Called X(int=%d): j=%d\n", i, j); } //1 > X(char *cp, int i) { > *this = X(i); > printf("Called X(char *cp= '%s', int=%d): j=%d\n", cp, i, j); //2 > } >}; > >main() { > X x("test", 5); >} Although this works in principle, one should be aware of the fact that X(i) on the right hand side of the assignment usually generates a temporary anonymous instance of X which is copied to the lhs and then destroyed. So this might be an expensive way of doing initialization. However, some compilers might optimize it. Exchanging the lines //1 and //2 by X(int i) : j(i) { printf("Called X(int=%d): j=%d this=%ld\n", i, j, this); }//1 printf("Called X(char *cp= '%s', int=%d): j=%d this=%ld\n", cp, i, j, this);//2 yields Called X(int=5): j=5 this=84813140 Called X(char *cp= 'test', int=5): j=5 this=84813136 using Zortech (2 distinct objects are involved) but results in Called X(int=5): j=5 this=-134218552 Called X(char *cp= 'test', int=5): j=5 this=-134218552 using g++ (only 1 object). Martin Hitz (hitz@csi.UOttawa.CA)
bjaspan@athena.mit.edu (Barr3y Jaspan) (04/15/91)
|> > >> Can I call one constructor to class X explicitly from within another |> > >> constructor to class X ? |> |> > >No. |> |> > Yes, by using operator= |> |> Sorry, but still no. Perhaps I don't understand the question (I don't recall the original posting), but this sounds like it applies (from the last annotation in ARM, section 12.1, p. 267 in my copy): "Calling a constructor from within a constructor is allowed, but often what it does isn't what the user expected. [code deleted] Such code is sometimes written by programmers who want to factor out the common parts of a set of constructors ... the way to factor out initialization code is to have an explicit initialization function that is not a constructor." -- Barr3y Jaspan, bjaspan@mit.edu Watchmaker Computing
pal@xanadu.wpd.sgi.com (Anil Pal) (04/15/91)
In article <20204@alice.att.com>, ark@alice.att.com (Andrew Koenig) writes: |> |> The original question was whether it was possible for one constructor |> of a class to call another constructor to help it construct the |> same object; the answer to that question is `no.' How about calling operator new using the placement syntax (and placing the object at "this")? Thus: MyClass::MyClass(/* some parameters */) { // first call basic constructor new (this) MyClass(/* other parameters */); // Then do stuff specific to this constructor ... } On reading the ARM, it appears that one must define an appropriate operator new in order to use placement syntax. This is fairly trivial in this case: void* operator new(size_t, void* p) { return p; } I had originally thought that the pre-defined global operator new included placement syntax, but the ARM indicates that I am mistaken. Is there a reason why the definition above could (or should) not be included in the language? -- Anil A. Pal, Silicon Graphics, Inc. pal@wpd.sgi.com (415)-335-7279
haydens@natasha.juliet.ll.mit.edu (Hayden Schultz) (04/17/91)
>On reading the ARM, it appears that one must define an appropriate >operator new in order to use placement syntax. This is fairly trivial >in this case: Why do you have to redefine the operator new? Here's a simple example which does it with a parent class and a derived child class. If there's no inheratance relationship, you could do the same thing if you created a conversion operator from C to P. include <stream.h> class P { int q; public: P() { q = 5; } P(int i); virtual void x() { cout << "parent class\n" << flush; }; }; class C : public P { public: void x() { cout << "child class\n" << flush; } }; P::P(int i) { this = new C; } main() { P p; p.x(); } Hayde Schultz (haydens@juliet.ll.mit.edu)
pal@xanadu.wpd.sgi.com (Anil Pal) (04/18/91)
In article <HAYDENS.91Apr17113619@natasha.juliet.ll.mit.edu>, haydens@natasha.juliet.ll.mit.edu (Hayden Schultz) writes: |> Why do you have to redefine the operator new? Here's a simple example |> which does it with a parent class and a derived child class. If |> there's no inheratance relationship, you could do the same thing if |> you created a conversion operator from C to P. [ example deleted ] The example uses assignment to this, which is deprecated in C++ 2.0 and beyond (in fact, using the +p option to cfront-based C++ translators will not allow the assignment. In any case, the point was to use a constructor for a class from within another constructor for the same class. Adding a superfluous class to the inheritance hierarchy seems like way too much work to do. As has been pointed out to me, I was mistaken in my reading of the ARM; the placement syntax of operator new is part of C++ 2.0 and beyond, but requires the inclusion of <new.h>. Given that, I believe that my original suggestion, calling the constructor through a placed new, is a reasonable solution. At least, I haven't heard any strong arguments against it so far. class X { X(); // Basic constructor X(int i); // Constructor that wants to call X::X() }; #include <new.h> X::X(int i) { new (this) X; // Whatever additional processing is desired } -- Anil A. Pal, Silicon Graphics, Inc. pal@wpd.sgi.com (415)-335-7279
steve@taumet.com (Stephen Clamage) (04/19/91)
haydens@natasha.juliet.ll.mit.edu (Hayden Schultz) writes: >Why do you have to redefine the operator new? Here's a simple example >which does it with a parent class and a derived child class... >class P { ... P(int i); ... }; >class C : public P { ... }; >P::P(int i) { > this = new C; >} This seems like a poor idea (it's not nice to fool the compiler), and will not work in general. If the object were not created with 'new', the object would appear to have two addresses, depending on what code was being exectued. In any event, the compiler will have the wrong idea about the size of the object. Finally, assigning to 'this' is an obsolescent feature, and will not be supported at some time in the future. -- Steve Clamage, TauMetric Corp, steve@taumet.com
paul@gill.UUCP (Paul Nordstrom) (04/23/91)
In article <20204@alice.att.com> ark@alice.UUCP () writes: >In article <17400@sunquest.UUCP> francis@sunquest.UUCP (Francis Sullivan) writes: >> ark@alice.att.com (Andrew Koenig) writes: > >> >In article <1991Apr2.110623.22219@and.cs.liv.ac.uk> markr@and.cs.liv.ac.uk writes: > >> >> Can I call one constructor to class X explicitly from within another >> >> constructor to class X ? > >> >No. > >> Yes, by using operator= > >Sorry, but still no. > >The usage you propose is implicitly (not explicitly) calling the constructor >to construct a *different* object. > >The original question was whether it was possible for one constructor >of a class to call another constructor to help it construct the >same object; the answer to that question is `no.' >-- > --Andrew Koenig > ark@europa.att.com I'm sure I'm missing something, but why does the following not imply an affirmative answer to markr's question? X::X() { new (this) X(1); // use some other constructor ... } X::X(int j) { // some other constructor } -- -- Paul Nordstrom Gill & Co., L.P. uunet!gill!paul
pal@xanadu.wpd.sgi.com (Anil Pal) (04/30/91)
In article <71952@microsoft.UUCP>, jimad@microsoft.UUCP (Jim ADCOCK) writes: |> In article <1991Apr18.005426.21863@dragon.wpd.sgi.com> pal@wpd.sgi.com writes: |> |X::X(int i) { |> | new (this) X; |> | // Whatever additional processing is desired |> |} |> |> I think this approach is generally a bad idea -- especially if the class |> has any base classes or any members that require construction. If so, |> this approach will result in any base classes being initialized twice, |> and any members being initialized twice. An excellent point. Quite apart from the efficiency issues of double initialization, there would be only a single destructor called, even though the constructor for the base classes or members would be called twice. This could result in serious problems (incorrect reference counts, failure to free memory, etc.) Accordingly, I would withdraw my suggestion, and advise against using placement syntax on operator new() as a method of factoring out common initialization. -- Anil A. Pal, Silicon Graphics, Inc. pal@wpd.sgi.com (415)-335-7279
rae@alias.com (Reid Ellis) (05/01/91)
Jim ADCOCK <jimad@microsoft.UUCP> writes: >Again, if you want to factor out common code from constructors, I recommend >you follow Bjarne's suggestion: write a private init() routine which is >called from within each constructor. But this does not work for constant objects. What then? Reid -- Reid Ellis 1 Trefan Street Apt. E, Toronto ON, M5A 3A9 rae@utcs.toronto.edu || rae@alias.com CDA0610@applelink.apple.com || +1 416 362 9181 [work]
jimad@microsoft.UUCP (Jim ADCOCK) (05/07/91)
In article <1991Apr30.211213.15118@alias.com> rae@alias.com (Reid Ellis) writes: |Jim ADCOCK <jimad@microsoft.UUCP> writes: |>Again, if you want to factor out common code from constructors, I recommend |>you follow Bjarne's suggestion: write a private init() routine which is |>called from within each constructor. | |But this does not work for constant objects. What then? In order of increasing evilness some ideas are: 1) You can still use an init() function per const member variable using the : syntax [but this would become tedious if many members had to be initialized this way.] 2) You can put a bunch of the const member variables in a struct that can then be init'ed as a unit. 3) You can fake init() using a macro that then textually substitutes in the right : syntax. 4) You can write a "const" init() function that then casts-away constnesses. I consider #4 pretty evil -- but still a lot better than the doubled- construction hack.
jamshid@ut-emx.uucp (Jamshid Afshar) (05/17/91)
In article <DSOUZA.91May3150307@gwen.cad.mcc.com> dsouza@gwen.cad.mcc.com (Desmond Dsouza) writes: >All data members which are objects have to be initialized before >the body of the constructor. There is this basic problem with >computing arguments for constructors in the initialization list: > > You cannot compute any temporaries (e.g. common subexpressions) > which are needed for initialization of such data members. <A stripped-down version of his example follows.> struct A { A(int m, int n); }; struct B { A a1; B(int j) : a1( F1(j), F1(j)+1 ) {} // F1(j) is very time-cosuming }; >// Here is what I would like to do, in a completely fictitous >// and probably highly ambiguous syntax: > >B::B(int j) >: > { int f1 = F1(j); > a1(f1, f1+1); > } >{ > .. actual constructor body >} > >Using globals, "memoizing" functions, etc. is not a solution to this. > >Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com When I need to do general compuation during class member initialization I use static member functions, but they can't help in your example. I encountered a similar problem in which I had a Base class constructor which took two parameters and a Derived class always passed it duplicate items (ie. Base(foo(j), foo(j))) where foo(j) was time-consuming. What I did was add another constructor to Base which only took one parameter. This was a good move as it turned out I needed to do this kind of construction just as often as the original way. Another solution if you do not want to mess with the Base class is to create an intermediate class. One solution to your example is to define class A above to be: struct A { A(int m, int n); A(int m); // construct an A whose n is one more than m }; Yes, I know this is obvious and it's not always so simple, but I've always found a solution like this to not only work, but actually end up reflecting the problem better. I like the current constructor format: Class(parms) : Base(params), member1(params), member2(params) { ...body... } because it makes it clear that you are constructing objects as opposed to executing statements in the member initialization list. When you are inside curly-braces you shouldn't have to worry whether an object has actually been constructed (which is one reason I believe goto's were restricted in C++). I think introducing two blocks for constructors would lead to errors using objects before they are constructed. Actually, this is already a problem because at least my compiler (Borland C++ 2.0) does not catch the use of an object before it's constructor call in the member initalization list. I don't believe the ARM requires it to be flagged as an error, but it sure would be a nice warning (also remember members are initalized in declaration order, not their order in the member initalization list). Speaking of special warnings for constructors, the use of virtual functions in a constructor would also be nice. Example taken from BC++ bug list at sun.soe.clarkson.edu in the file ~ftp/pub/Turbo-C++/bug-report. class String { char* s; unsigned Len; public: String(const char* p); // char* --> String //... }; String::String(const char* p) : Len(strlen(p)), s(new char[Len+1]) { /////// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ strcpy(s, p); /////// Missed feature: TC++ should warn here } /////// Ex: `Len used before it is initialized' Jamshid Afshar jamshid@emx.utexas.edu
davidm@uunet.UU.NET (David S. Masterson) (05/18/91)
>>>>> On 3 May 91 20:03:07 GMT, dsouza@gwen.cad.mcc.com (Desmond Dsouza) said: Desmond> In article <71952@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) Desmond> writes: Jim> Again, if you want to factor out common code from constructors, I Jim> recommend you follow Bjarne's suggestion: write a private init() routine Jim> which is called from within each constructor. Desmond> Sorry, but I dont think this works in general. Desmond> All data members which are objects have to be initialized before Desmond> the body of the constructor. There is this basic problem with Desmond> computing arguments for constructors in the initialization list: Desmond> You cannot compute any temporaries (e.g. common subexpressions) which Desmond> are needed for initialization of such data members. Desmond> class A { public: A(int m, int n); }; Desmond> class B { Desmond> public: Desmond> B (int j); Desmond> private: Desmond> A a1; // initialized with F1(j), F1(j)+1 for Desmond> //some horribly complex F1 Desmond> }; Desmond> B::B(int j) Desmond> // how can I share the computation of F1(j) here ??? Desmond> : a1 (F1(j), F1(j)+1) Desmond> { Desmond> // this part does not help: I cannot modify a1 at this point. Desmond> } WARNING: Novice code coming... Would a possible solution to this be: B::B(int j) : a1() { int x = F1(j), y = x + 1; A temp(x,y); a1 = temp; // why can't you modify a1 using copy assignment? } -- ==================================================================== David Masterson Consilium, Inc. (415) 691-6311 640 Clyde Ct. uunet!cimshop!davidm Mtn. View, CA 94043 ==================================================================== "If someone thinks they know what I said, then I didn't say it!"
dsouza@gwen.cad.mcc.com (Desmond Dsouza) (05/21/91)
cimshop!davidm@uunet.UU.NET (David S. Masterson) writes: Desmond> You cannot compute any temporaries (e.g. common subexpressions) which Desmond> are needed for initialization of such data members. Desmond> class A { public: A(int m, int n); }; Desmond> class B { Desmond> public: Desmond> B (int j); Desmond> private: Desmond> A a1; // initialized with F1(j), F1(j)+1 for Desmond> //some horribly complex F1 Desmond> }; Desmond> B::B(int j) Desmond> // how can I share the computation of F1(j) here ??? Desmond> : a1 (F1(j), F1(j)+1) Desmond> { Desmond> // this part does not help: I cannot modify a1 at this point. Desmond> } WARNING: Novice code coming... Would a possible solution to this be: B::B(int j) : a1() { int x = F1(j), y = x + 1; A temp(x,y); a1 = temp; // why can't you modify a1 using copy assignment? } -- You make assumptions about default constructor (0 arguments) and the assignment operator which are not valid in general. Someone else suggested deriving a class from A and using that instead. That is a pretty expensive way of computing a temporary integer! -- Desmond. -- ------------------------------------------------------------------------------- Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com | Phone: [512] 338-3324 Box 200195, Austin, TX 78720 | UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!dsouza
jimad@microsoft.UUCP (Jim ADCOCK) (05/21/91)
In article <DSOUZA.91May3150307@gwen.cad.mcc.com> dsouza@gwen.cad.mcc.com (Desmond Dsouza) writes: > You cannot compute any temporaries (e.g. common subexpressions) > which are needed for initialization of such data members. Consider: ---- int F1(int j) { /* something really really big and horrible here */ return j;} class A { public: A(int m, int n); }; class B { public: B (int j); private: int f1; A a1; // initialized with F1(j), F1(j)+1 for some horribly complex F1 }; B::B(int j) // how can I share the computation of F1(j) here ??? !!! Try the following: : f1(F1(j)), a1(f1, f1+1) { // this part does not help: I cannot modify a1 at this point. }
dsouza@gwen.cad.mcc.com (Desmond Dsouza) (05/23/91)
In article <72461@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: > You cannot compute any temporaries (e.g. common subexpressions) > which are needed for initialization of such data members. Consider: ---- int F1(int j) { /* something really really big and horrible here */ return j;} class A { public: A(int m, int n); }; class B { public: B (int j); private: int f1; A a1; // initialized with F1(j), F1(j)+1 for some horribly complex F1 }; B::B(int j) // how can I share the computation of F1(j) here ??? !!! Try the following: : f1(F1(j)), a1(f1, f1+1) { // this part does not help: I cannot modify a1 at this point. } No good. You cannot afford to add a data member per object instance to simply store an intermediate constructor variable. (There is also the subtle point about order of initialization of data members -- it follows the order in the class definition, so you had it right). -- Desmond. -- ------------------------------------------------------------------------------- Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com | Phone: [512] 338-3324 Box 200195, Austin, TX 78720 | UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!dsouza
euaeny@eua.ericsson.se (Erik Nyquist) (05/24/91)
dsouza@gwen.cad.mcc.com (Desmond Dsouza) writes: >In article <72461@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: > > You cannot compute any temporaries (e.g. common subexpressions) > > which are needed for initialization of such data members. > Consider: > ---- > int F1(int j) { /* something really really big and horrible here */ return j;} > class A { > public: A(int m, int n); > }; > class B { > public: > B (int j); > private: > int f1; > A a1; // initialized with F1(j), F1(j)+1 for some horribly complex F1 > }; > B::B(int j) > // how can I share the computation of F1(j) here ??? !!! Try the following: > : f1(F1(j)), a1(f1, f1+1) > { > // this part does not help: I cannot modify a1 at this point. > } >No good. You cannot afford to add a data member per object instance to >simply store an intermediate constructor variable. >(There is also the subtle point about order of initialization of data > members -- it follows the order in the class definition, so you had it right). Why no extend the language and make it possible to declare temporaries in the initialization list? ;-) (yes, I know! We should try to standardize the language, not change it.) class A { public: A(int m, int n); }; class B { public: B (int j); private: A a1; // initialized with F1(j), F1(j)+1 for some horribly complex F1 }; B::B(int j) // how can I share the computation of F1(j) here ??? !!! Is this what you need? : int f1 = F1(j), a1(f1, f1+1) { // this part does not help: I cannot modify a1 at this point. } I don't say that we should change the language, but how can we solve this problem without language extensions? We could also ask ourselves if we really should try to solve this problem. That surely depends on how common it is! Erik Nyquist Ellemtel Utecklings AB We are no longer the knights that say Ni! Box 1505 We are the knights that say: S-125 25 Alvsjo, Sweden Iky,iky,iky,iky,patang,zoop-boing, zowie. -- Erik Nyquist Ellemtel Utecklings AB We are no longer the knights that say Ni! Box 1505 We are the knights that say: S-125 25 Alvsjo, Sweden Iky,iky,iky,iky,patang,zoop-boing, zowie.
marc@mit.edu (Marc Horowitz) (05/28/91)
In article <1991May23.170530.23037@eua.ericsson.se> euaeny@eua.ericsson.se (Erik Nyquist) writes: dsouza@gwen.cad.mcc.com (Desmond Dsouza) writes: >In article <72461@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: > > You cannot compute any temporaries (e.g. common subexpressions) > > which are needed for initialization of such data members. I don't say that we should change the language, but how can we solve this problem without language extensions? We could also ask ourselves if we really should try to solve this problem. That surely depends on how common it is! I posted to comp.lang.c++ last night with almost exactly the same problem. A local friend mentioned that this issue had been recently discussed, so I'm responding to that original post. I I'll start by saying that this problem should definitely be looked at. It apparently happens at least somewhat often, since it happened to me last night. I had an even more difficult situation. My classes look like this: class A { public: A(int a, int b, int c, int d); // ... }; class B : public A { public: B(char *s) :A(/* ??? */) { /* too late */ } // ... }; void f(const char *s, int *a, int *b, int *c, int *d); f takes a string, and parses the integers out of it. It's somewhat expensive. Now, how should I go about doing this? Every solution I've been able to come up with is a morally abhorrent kludge involving some large number of static variables. I really would like to be able to have a block where I could define a few local temporaries, call f, and pass those temps into the base constructor. I could add another constructor to A, but A shouldn't need to depend on f, since f is really a part of B. Also note that subclassing won't help, since the arguments to A aren't functions of a common temporary, as f(x) and f(x)+1 are in the original example. I agree with Desmond: this seems like a definite flaw in the language. Someone mentioned that adding another block where temporaries could be created could result in programmers accidentally thinking the object was constructed while writing code in this block. I think this problem is solvable. If the block is in a scope similar to that of a static member function (e.g., ``this'' is not defined), then any use of a non-static member is an error. I think this is the way it should be, and is not overly restrictive. As long as someone can come up with a non-ambiguous syntax, it doesn't seem that code generation should be too difficult, either. Marc -- Marc Horowitz <marc@mit.edu> 617-253-7788
Marc.Horowitz@sunbrk.FidoNet.Org (Marc Horowitz) (05/28/91)
Reply-To: euaeny@eua.ericsson.se's message of 23 May 91 17:05:30 GMT In article <1991May23.170530.23037@eua.ericsson.se> euaeny@eua.ericsson.se (Erik Nyquist) writes: dsouza@gwen.cad.mcc.com (Desmond Dsouza) writes: >In article <72461@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: > > You cannot compute any temporaries (e.g. common subexpressions) > > which are needed for initialization of such data members. I don't say that we should change the language, but how can we solve this problem without language extensions? We could also ask ourselves if we really should try to solve this problem. That surely depends on how common it is! I posted to comp.lang.c++ last night with almost exactly the same problem. A local friend mentioned that this issue had been recently discussed, so I'm responding to that original post. I I'll start by saying that this problem should definitely be looked at. It apparently happens at least somewhat often, since it happened to me last night. I had an even more difficult situation. My classes look like this: class A { public: A(int a, int b, int c, int d); // ... }; class B : public A { public: B(char *s) :A(/* ??? */) { /* too late */ } // ... }; void f(const char *s, int *a, int *b, int *c, int *d); f takes a string, and parses the integers out of it. It's somewhat expensive. Now, how should I go about doing this? Every solution I've been able to come up with is a morally abhorrent kludge involving some large number of static variables. I really would like to be able to have a block where I could define a few local temporaries, call f, and pass those temps into the base constructor. I could add another constructor to A, but A shouldn't need to depend on f, since f is really a part of B. Also note that subclassing won't help, since the arguments to A aren't functions of a common temporary, as f(x) and f(x)+1 are in the original example. I agree with Desmond: this seems like a definite flaw in the language. Someone mentioned that adding another block where temporaries could be created could result in programmers accidentally thinking the object was constructed while writing code in this block. I think this problem is solvable. If the block is in a scope similar to that of a static member function (e.g., ``this'' is not defined), then any use of a non-static member is an error. I think this is the way it should be, and is not overly restrictive. As long as someone can come up with a non-ambiguous syntax, it doesn't seem that code generation should be too difficult, either. Marc -- Marc Horowitz <marc@mit.edu> 617-253-7788 * Origin: Seaeast - Fidonet<->Usenet Gateway - sunbrk (1:343/15.0)
pena@brainware.fi (Olli-Matti Penttinen) (05/30/91)
In article <MARC.91May27140938@steve-dallas.mit.edu> marc@mit.edu (Marc Horowitz) writes: [ lotsa stuff zapped ] I'll start by saying that this problem should definitely be looked at. It apparently happens at least somewhat often, since it happened to me last night. I had an even more difficult situation. My classes look like this: class A { public: A(int a, int b, int c, int d); // ... }; class B : public A { public: B(char *s) :A(/* ??? */) { /* too late */ } // ... }; void f(const char *s, int *a, int *b, int *c, int *d); f takes a string, and parses the integers out of it. It's somewhat expensive. Now, how should I go about doing this? Every solution I've been able to come up with is a morally abhorrent kludge involving some large number of static variables. I really would like to be able to have a block where I could define a few local temporaries, call f, and pass those temps into the base constructor. I could add another constructor to A, but A shouldn't need to depend on f, since f is really a part of B. Also note that subclassing won't help, since the arguments to A aren't functions of a common temporary, as f(x) and f(x)+1 are in the original example. Do you ever use f for anything but to initialize or change the value of some A. If not, then f would really belong in A. In anycase, why not add A::A(const char *); that uses f to compute the 4 ints. Besides, Bjarne's proposal to use a protected init-method works well, too, iff A has a default constructor. Admittedly, things might get a little dirty if an A cannot have a meaningful state constructed out of nothing. In that case, one could (accidentally) use the value of an A before the object is fully constructed, so the problem would still be there. All in all, I don't see a reason to extend the language just because a new feature would occasionally save a few keystrokes. [ more text deleted ] Marc -- Marc Horowitz <marc@mit.edu> 617-253-7788 ==pena -- Olli-Matti Penttinen <pena@brainware.fi> | "When in doubt, use brute force." Brainware Oy | --Ken Thompson P.O.Box 330 +---------------------------------- 02151 ESPOO, Finland Tel. +358 0 4354 2565 Fax. +358 0 461 617
niklas@appli.se (Niklas Hallqvist) (06/02/91)
pena@brainware.fi (Olli-Matti Penttinen) writes: :In article <MARC.91May27140938@steve-dallas.mit.edu> marc@mit.edu (Marc Horowitz) writes: : [ lotsa stuff zapped ] : I'll start by saying that this problem should definitely be looked at. : It apparently happens at least somewhat often, since it happened to me : last night. I had an even more difficult situation. My classes look : like this: : class A { : public: : A(int a, int b, int c, int d); : // ... : }; : class B : public A { : public: : B(char *s) : :A(/* ??? */) : { /* too late */ } : // ... : }; : void f(const char *s, int *a, int *b, int *c, int *d); : f takes a string, and parses the integers out of it. It's somewhat : expensive. : Now, how should I go about doing this? Every solution I've been able : to come up with is a morally abhorrent kludge involving some large : number of static variables. I really would like to be able to have a : block where I could define a few local temporaries, call f, and pass : those temps into the base constructor. I could add another : constructor to A, but A shouldn't need to depend on f, since f is : really a part of B. Also note that subclassing won't help, since the : arguments to A aren't functions of a common temporary, as f(x) and : f(x)+1 are in the original example. :Do you ever use f for anything but to initialize or change the value :of some A. If not, then f would really belong in A. In anycase, why :not add A::A(const char *); that uses f to compute the 4 ints. :Besides, Bjarne's proposal to use a protected init-method works well, :too, iff A has a default constructor. Admittedly, things might get a :little dirty if an A cannot have a meaningful state constructed out of :nothing. In that case, one could (accidentally) use the value of an A :before the object is fully constructed, so the problem would still be :there. :All in all, I don't see a reason to extend the language just because :a new feature would occasionally save a few keystrokes. : [ more text deleted ] You forgot to read the requirements of the problem. The class A and the function f could not be altered (probably due to a binary only license of some library). The way to do this is of course by using MI, private inheritance and the well-known dependence of base class declaration order ;-) class A { public: A(int a, int b, int c, int d); // ... }; void f(const char* s, int* a, int* b, int* c, int* d); class B_helper { protected: int a, b, c, d; B_helper(const char* s) { f(s, &a, &b &c, &d); } }; class B : private B_helper, public A { public: B(const char* s) : B_helper(s), A(a, b, c, d) { /* usual code */ } // ... }; This code is untested by me, but I did send this suggestion to Marc a few days ago, and as he has not (yet) replied negatively, I suppose it works. If it does, this should be general enough to solve any problem of this kind, isn't it so? Marc! I got your reply, have you tested this code yet? Niklas -- Niklas Hallqvist Phone: +46-(0)31-40 75 00 Applitron Datasystem Fax: +46-(0)31-83 39 50 Molndalsvagen 95 Email: niklas@appli.se S-412 63 GOTEBORG, Sweden mcsun!sunic!chalmers!appli!niklas
pena@brainware.fi (Olli-Matti Penttinen) (06/03/91)
In article <1389@appli.se> niklas@appli.se (Niklas Hallqvist) writes: I wrote :Besides, Bjarne's proposal to use a protected init-method works well, :too, iff A has a default constructor. Admittedly, things might get a :little dirty if an A cannot have a meaningful state constructed out of :nothing. In that case, one could (accidentally) use the value of an A :before the object is fully constructed, so the problem would still be :there. :All in all, I don't see a reason to extend the language just because :a new feature would occasionally save a few keystrokes. : [ more text deleted ] Niklas You forgot to read the requirements of the problem. The class A and the function f could not be altered (probably due to a binary only license of some library). The way to do this is of course by using MI, private inheritance and the well-known dependence of base class declaration order ;-) class A { public: A(int a, int b, int c, int d); // ... }; void f(const char* s, int* a, int* b, int* c, int* d); class B_helper { protected: int a, b, c, d; B_helper(const char* s) { f(s, &a, &b &c, &d); } }; class B : private B_helper, public A { public: B(const char* s) : B_helper(s), A(a, b, c, d) { /* usual code */ } // ... }; -- Niklas Hallqvist Phone: +46-(0)31-40 75 00 Applitron Datasystem Fax: +46-(0)31-83 39 50 Molndalsvagen 95 Email: niklas@appli.se S-412 63 GOTEBORG, Sweden mcsun!sunic!chalmers!appli!niklas Well, I wasn't aware of that A couldn't be altered. And I'm sorry I stated my case poorly. My real objective was to show that whoever wrote A in the first place, should have allowed for A::A() and a protected init-method, which could be used by A's children to constrct an A form any set of values whatsoever. I still don't agree with the proposition to extend the language because of existing poor programming practices. Your solution probably works (I haven't tested it either), but as you said yourself, it is a horrible kludge only very few programmers understand, let alone be able to use themselves. ==pena -- Olli-Matti Penttinen <pena@brainware.fi> | "When in doubt, use brute force." Brainware Oy | --Ken Thompson P.O.Box 330 +---------------------------------- 02151 ESPOO, Finland Tel. +358 0 4354 2565 Fax. +358 0 461 617