3
votes

I have a in-class initialized const member in a derived class which I'd like to pass to the constructor of the base class.

Example:

class Base{
public:
    Base(int a) : i(a){}
private:
    int i;
};

class Derived : Base{
public:
    Derived() : Base(a){}
private:
    const int a = 7;
};

int main(){
    Derived d;
}

However this spawns an uninitialized error:

field 'a' is uninitialized when used here [-Wuninitialized]

I was under the impression that const initializing it would set the value directly allowing it to be passed from the derived ctor in this manner. Am I doing something wrong or am I under the wrong impression? When are the const in-class initialized members initialized?

2
Make it static const or constexpr.Captain Obvlious
What's the point of a non-static constant member that can only ever be initialized to a single value? If, on the other hand, the value is given by a constructor argument, you can simply use that argument rather than the class member.Kerrek SB
Granted, @KerrekSB, that is true. But after I realized this it got me curious to why this did not work, and also, I get a linker error when trying to make it static constexpr. The actual member has a constexpr constructorParham

2 Answers

7
votes

When initializing base classes and class members during creation of an object, the order of initialization is:

  • Virtual base classes, in tree order
  • Direct non-virtual base classes, in order
  • Non-static data members, in their order of declaration.

So Base(a) happens before a = 7 happens.

One way to fix this would be to make a be static const or static constexpr. This is probably a good idea anyway, because non-static const variables make your class more difficult to use. (e.g. there will not be an implicitly-generated copy-constructor).

3
votes

Your question,

When are the const in-class initialized members initialized?

is a bit of a red herring. "in-class initialized" doesn't really mean anything; the brace-or-equal initializer is essentially just syntactic sugar and takes the place of the corresponding constructor initalizer list slot. const also has no special bearing. So the real question should be:

When are non-static data members initialized?

The details don't actually matter so much, suffice to say that non-static data members are initialized after base subobjects are initialized, so your proposed construction cannot work.

The straight-forward answer is not to use a brace-or-equals-initializer and just use a normal (possibly defaulted) constructor parameter. Here are a few examples:

struct Foo : Base
{
    const int a;

    // Default constructor, mention value only once
    Foo(int _a = 10) : Base(_a), a(_a) {}

    // DRYolent default constructor
    Foo() : Base(10), a(10) {}

    // Delegating default constructor
    Foo() : Foo(10) {}
    private: Foo(int _a) : Base(_a), a(_a) {}
};

Alternatively, if the value of the constant doesn't need to be configurable, then you can make it a per-class constant (rather than per-object):

struct Foo : Base
{
    static const int a = 10;
    Foo() : Base(a) {}
};

const int Foo::a;  // only if you ODR-use Foo::a