Nico Schlömer
Nico Schlömer

Reputation: 58951

Using derived class members for the initialization of the parent class

I would like to initialize a class B that's derived from class A, and where in B I construct a cache first that is used with the construction of A, e.g.,

class B: public A {

public:
  B(): A(cache_), cache_(this->buildCache_())

protected:
  const SomeDataType cache_;

private:
  SomeDataType
  buildCache_() const
  {
    // expensive cache construction
  }

}

This is not going to work though because the parent object A is always initialized first (before cache_ is filled).

(For the sake of completeness: The cache_ is used many more times in classes derived from B.)

As an alternative, I could do

class B: public A {

public:
  B(): A(this->buildCache_())

protected:
  const SomeDataType cache_;

private:
  SomeDataType
  buildCache_()
  {
    // expensive cache construction
    // fill this->cache_ explicitly
    return cache_;
  }

}

This has the disadvantage that buildCache_() can't be const. Also, GCC complains that

warning: ‘B::cache_’ should be initialized in the member initialization list [-Weffc++]

Is there a more appropriate solution to this?

Upvotes: 2

Views: 97

Answers (2)

Jarod42
Jarod42

Reputation: 218323

From C++11, you may use forward constructor:

class B: public A
{
public:
  B(): B(B::buildCache_()) {}

private:
  B(SomeDataType&& cache) : A(cache), cache_(std::move(cache)) {}

private:
  static SomeDataType buildCache_()
  {
    // expensive cache construction
  }

protected:
    const SomeDataType cache_;
};

Upvotes: 1

Barry
Barry

Reputation: 304152

What you are doing as-is is undefined behavior, from [class.base.init]/14, emphasis mine:

Member functions (including virtual member functions, 10.3) can be called for an object under construction. Similarly, an object under construction can be the operand of the typeid operator (5.2.8) or of a dynamic_cast (5.2.7). However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the result of the operation is undefined.

What you want to do instead is use the Base-from-Member idiom:

class Cache {
protected:
    const SomeDataType cache_;
};

class B : private Cache, public A {
public:
    B() : Cache(), A(cache_)
    { }
};

Upvotes: 4

Related Questions