Consider this:

class Foo {
    private:
        Bar x;

    public:
        Foo(int a) { // no initialization here since constructor is dependent on a following if-block
            if (a==0) x=Bar(12,'a', 34);  // some (int, char, int) constructor of Bar
            else x=Bar(13); // (int) constructor of Bar
      }
}

What this is supposed to do, is check the value of parameter a, and initialize Bar x with a certain constructor and certain parameters depending on a's value. The problem is however, that this is of course read by the compiler as

        Foo(int a): Bar() { // !
            if (a==0) x=Bar(12,'a', 34);  // some Bar(int, char, int) constructor of Bar
            else x=Bar(13); // Bar(int) constructor of Bar
        }

The compiler adds Bar() to the initialization list, since I ommited it. It's now doubly initialized, once with its () constructor and once in the function body. But what if initializing Bar (even with its default constructor) is very costly (regarding performance) and I can't or don't want this double-initialization ?

How can I NOT initialize Bar x, until IN the actual constructor body? And if it's not possible, how would I best work around this problem?

Adding to this: Why was C++ designed in this way? Why does it force initialization of members before the actual constructor body?

6

There are 6 answers

1
Fabio Fracassi On BEST ANSWER

I think you are asking the wrong question, instead of trying to inhibit initialization, you should just do it, i.e. spell your ctor as:

Foo(int a) : x((a==0) ? Bar(12,'a', 34) : Bar(13)) {}

This will not cause any copies or moves (see here), and is as idiomatic as it gets.

0
leslie.yao On

No, you can't. The members are guaranteed to be initialized yet, when in the ctor body.

The whole initialization order is well defined as:

  • First, the most derived class's constructor calls the constructors of the virtual base class subobjects. Virtual base classes are initialized in depth-first, left-toright order.
  • Next, direct base class subobjects are constructed in the order they are declared in the class definition.
  • Next, (nonstatic) member subobjects are constructed in the order they were declared in the class definition.
  • Finally, the body of the constructor is executed.
3
Matt Olson On

You could make x a std::unique_ptr<Bar> and then allocate it as desired in the ctor.

2
M.M On

C++ was designed this way so that the constructor actually constructs and initializes things, instead of letting you shoot yourself in the foot.

Nevertheless, since C++11 there is now a backdoor foot-shooting technique that you can use: members of anonymous unions do not get constructed unless you explicitly construct them. The code would be:

class Foo
{
    union 
    {
        Bar x;
    };

public:
    Foo(int a)
    {
        if (a==0) 
            new(&x) Bar(12,'a', 34);
        else
            new(&x) Bar(13);
    }

    ~Foo()
    {
         x.~Bar();
    }
};

To be clear this should really be a last resort. The compiler-generated copy-constructor, move-constructor etc. get defined as deleted so you would need to implement them all too if you wanted those operations available.

8
M.M On

Here is a solution using delegating constructors:

class Foo
{
    Bar b;
    Foo( Bar b ): b(std::move(b)) { }

public:
    Foo(int a): Foo( a ? Bar(13) : Bar(12, 'a', 34) ) {}
};

The desired object is created during delegation and then a constructor which takes a Bar is invoked. If you want to allow anyone to construct a Foo from a Bar then you could make that constructor public.

0
T.C. On

Slightly more expensive memory-wise than the anonymous union solution, but much less error-prone and doesn't incur an extra copy/move or dynamic allocation.

class Foo {
    private:
        std::experimental::optional<Bar> x;

    public:
        Foo(int a) {
            if (a==0) x.emplace(12,'a', 34);  // some (int, char, int) constructor of Bar
            else x.emplace(13); // (int) constructor of Bar
      }
};

Also, in this case x would be used like a pointer: x->some_Bar_function() and some_function_taking_a_Bar(*x).