3

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?

Parham
  • 3,157
  • 4
  • 31
  • 44
  • 3
    Make it `static` `const` or `constexpr`. – Captain Obvlious Apr 09 '15 at 22:48
  • 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 Apr 09 '15 at 22:56
  • 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` constructor – Parham Apr 09 '15 at 22:59

2 Answers2

7

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).

M.M
  • 138,810
  • 21
  • 208
  • 365
  • By "virtual base class" do you mean "virtual base subobjects"? Classes don't get initialized; objects do. – Kerrek SB Apr 09 '15 at 22:52
  • 1
    @KerrekSB yes, I'm using the normal sloppy terminology – M.M Apr 09 '15 at 22:53
  • And what is this "tree order"? Is there some inheritance diagram that's guaranteed to form a tree? – Kerrek SB Apr 09 '15 at 22:53
  • 1
    @KerrekSB it's paraphrasing of "virtual base classes are initialized in the order in which they appear in depth-first left-to-right traversal of the base class declarations (left-to-right refers to the appearance in base-specifier lists)" . This isn't relevant to OP's problem so I didn't want to obfuscate my answer with a language lawyer level of detail. Feel free to post your own answer :) – M.M Apr 09 '15 at 22:55
  • Thank you! I tried doing this but it results in a linker error in the derived CTOR where I try to pass it to the base class CTOR. Any ideas? Also, the actual field is a `constexpr` so I am trying to make it `constexpr static` – Parham Apr 09 '15 at 22:56
  • @KerrekSB although I am not entirely sure where it is guaranteed that a static member must be initialized before this constructor is invoked (e.g. if there is a static `Derived` in another unit) – M.M Apr 09 '15 at 22:57
  • 1
    @Parham `constexpr static` is fine. For the linker error [see here](http://stackoverflow.com/questions/3025997/defining-static-const-integer-members-in-class-definition) – M.M Apr 09 '15 at 22:58
  • @MattMcNabb: I think that's fine, because the initialization has to happen before any function in the containing TU is odr-used, and the TU must contain the class definition, and hence the constructor. – Kerrek SB Apr 09 '15 at 23:04
3

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
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084