user9926339
user9926339

Reputation:

C++ - Is there a workaround to delegating constructors being not the only members in an initialization list

class Foo
{
public:
  Foo() {}
  Foo(int i) 
   : Foo(), m_bar(0) {}

private:
  int m_bar;
};

Is there a workaround to making such code valid?

Is there a way to have more than one member in the initialization list if we have a delegating constructor, and what is the reason for such a restriction on having the delegating constructor be the only member in the initialization list.

Upvotes: 1

Views: 1864

Answers (2)

Christophe
Christophe

Reputation: 73406

The rule that you refer to is defined in [class.base.init]/6:

A mem-initializer-list can delegate to another constructor of the constructor’s class using any class-or-decltype that denotes the constructor’s class itself. If a mem-initializer-id designates the constructor’s class, it shall be the only mem-initializer; the constructor is a delegating constructor, and the constructor selected by the mem-initializer is the target constructor. The target constructor is selected by overload resolution. Once the target constructor returns, the body of the delegating constructor is executed. If a constructor delegates to itself directly or indirectly, the program is ill-formed, no diagnostic required.

Suppose a moment that the the delegate constructor could be used together with other mem-initializers:

  • you could invoke two different delegates. But there can be only one construction. So you'd have a conflict of which one to chose.
  • with mem-initializers after the delegation, when the delegate would finish its job, the execution of the delegating constructor would be resumed not with the body but in the remaining mem-initializers.
  • But mem-initializers are not like assignments where one assignement could overwrite another. Mem-initializers invoke constructors of members. And member variables can be constructed only once. There would then be a conflict of how to initialize the members in the list, and which constructor to discard.
  • This is even worse, since both delegating and delegated constructors have to completely construct a class boject, i.e. all its members.

Here an example to clarify the problem with a member that can be initialized only once, and solutions:

class Foo
{
  const int m_bar;              // can be initalized only once 
  int m_zoo;                    // must be constructed (default possible) but can be overwritten
public:
  Foo(): m_bar(1), m_zoo(2) {}  // the constant can never be changed
  Foo(int i) 
   : Foo() {m_zoo=i;}           // you can still change in the body already constructed items
  Foo(int i, int j)            // comprehensive init 
    : m_bar(i), m_zoo(j) {}    // (has all that delegating may want(
  Foo(char a) 
    : Foo(a,2) {};             // delegate to the comprehensive ctor
};

Online demo

The delegation rule aims to keep all this simple. It just means that

  • there can be only one constructor that constructs the object and its members (either the constructor itself, or a delegate)
  • the delegating constructor just decides of the parameters of the delegate, and can add additional behavior in the constructor body
  • (and we all know that the body is invoked only once all the base classes and members are constructed)

So the way to make your design valid is to move all the mem-initilizer to the target constructor, and if needed use parameters of this target constructor to specify values for its mem-initializers. Or to overwrite in the body what was already initialized; but this is not always possible.

Upvotes: 3

Fire Lancer
Fire Lancer

Reputation: 30145

The problem is since the initializer list in a constructor must initialize every base and member (in declaration order) that has it's own constructor (default constructor if not otherwise specified), that : Foo() must have already done so, and calling some members constructor twice would be bad.

I suppose you could argue in the case of primitives that might be left uninitialized that it could be allowed, but then in such cases assigning them within the function body would be equivalent.

Upvotes: 3

Related Questions