IBM Z and LinuxONE - Languages - Group home

C++0x delegating constructors

  
Something that Java, C++/CLI, and C# already has, is called delegating constructor. This is the ability for one constructor to delegate to another within the same class.C++03 does not have this capability because you cannot name a constructor within the same class in one of its constructor's initializer list. It can use constructors of other class types, including base classes and member objects.

In fact, it causes a serious problem when people who learn these other language come to C++, and try using it because what looks reasonable, will compile and run, but does not do what they think it does.

And it is more common then you think, because in many cases constructors may differ by the parameter list with common bodies. If the constructor can use default arguments, you could common the bodies. If it can't then you are out of luck today:

class A {};//has a conversion operator to double
class C
{
int x;
double y;
void startup();
public: //C(): x(0), y(1.23) { startup();} // I can collapse this into the following constructor and common up the body
C(int i=0) : x(i), y(1.23) { startup();}
C (const A& a) : x(0), y(a) {startup();} // I can't with this one
};


So having successfully refactored once, you might decide to try it in another case, turning this code into:
class X {  
Y y_;
Z z_;
void init();
public:
X() { init() };
X( int i ) : y_( i ),z_(1.23) { init(); }
X( Widget w): y_(3.21), z_(1.23) { init(); }
};


this because this is similar to Java delegating constructors:

class X {  
Y y_;
Z z_;
void init();
public:
X(){ init(); };
X( int i ) : y_( i ),z_(1.23) { X(); }//I know both constructors are run because I can see it in a debugger
X( Widget w): y_(3.21), z_(1.23) { X(); } //same here
};


This should work. Or should it ?

Well of course it doesn't, and the Mystery#1 of the Week is can someone tell me why not?

So may be we want to try something even more ambitious:

class X {  
Y y_;
Z z_;
void init();
void *operator new(unsigned int, const X*);
... //assume we also have corresponding delete
public:
X(){ init(); };
X( int i ) : y_( i ),z_(1.23) { new (this) X; }//I know both constructors are run because I can see it in a debugger
X( Widget w): y_(3.21), z_(w) { new (this) X; } // Same here
};


This looks even more promising and in fact it does everything I want it to do. Seems clever as I am using a placement new on the same object. In reality, it is an even worst solution then before. Mystery#2 of the Week is why is this worst?

In fact C++03 had no better solution which is either more cumbersome, or error prone.

C++0x will fix this for the sake of improved teachability and library building. The solution is to simply allow the call to the target constructor in the initializer list of the delegating constructor. We call the caller the delegating constructor and the callee is the target constructor

class X {  
Y y_;
Z z_;
void init();
public:
X( int i, const Widget &w): y_(i),z_(w) { init();}
X(){ init(); };
X( int i ) : X( i, 1.23) { }
X( Widget w): X(3.21, w) { }
};


The final accepted paper is in:
n1986