2

Given the following base class:

class Base {
    int a, b;
public:
    Base(int a, int b=42): a(a), b(b) { }
};

And a class that is derived from base:

class Derived: public Base {
    using Base::Base; // inherit Base constructors
    bool c;
public:
    Derived(int a): Base(a), c(true) { }
    Derived(int a, int b): Base(a, b), c(true) { }
};

Is there a way to avoid having to create two separate constructors for Derived and instead overload the inherited Base constructors to initialize the extra member of Derived?

I was thinking to use something like this:

template <typename... Args, 
    typename std::enable_if_t<std::is_constructible_v<Base, Args&&...>, int> = 0>
explicit Derived(Args&&... args):
    Base(std::forward<Args>(args)...), c(true) {}

This is close, but is too verbose and does not work if the the base class's constructors are inherited. i.e. if using Base::Base is present in the class (then it defaults to those constructors and does not initialize the field b).

It works if the base class inherited constructors are not present i.e. removing using Base::Base.


Is this the only way to have this work? i.e. by removing using Base::Base and using a variadic template constructor in each derived class? Is there a less verbose way to overload the inherited constructors?

I am using C++17.

smac89
  • 39,374
  • 15
  • 132
  • 179
  • Do you really mean private derivation? Also, I don't think your base class can compile `error: 'Base::Base(int, int)' cannot be overloaded with 'Base::Base(int, int)'` –  Oct 08 '18 at 16:44
  • 1
    `Base(int, int) {...} Base(int, int=42) {...}` isn't legal. Can you show a more accurate representation? – NathanOliver Oct 08 '18 at 16:45
  • Is the question just, how do you initialize `b` to `true`? – Barry Oct 08 '18 at 16:46
  • @Barry pretty much yes – smac89 Oct 08 '18 at 16:47
  • @NeilButterworth, that should work now. I didn't test this and meant it more as pseudocode, but I will run it now – smac89 Oct 08 '18 at 16:49

2 Answers2

3

In this case all you need to do is provide c with an in class initializer

class Derived : public Base {
    using Base::Base;
    bool c = true;
};

allows you to use Base's constructors and will initialize c to true in all cases.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Thanks for your answer. I wasn't aware that the default initialization would occur after the base class members are initialized which is why I went this longer route of using variadic template constructors – smac89 Oct 08 '18 at 16:54
  • 2
    @smac89 All base classes are initialized before the derived object so you just get the right behavior. – NathanOliver Oct 08 '18 at 16:55
2

It seems like what you're looking for is a way to inherit Base's constructors while just initializing members of Derived to something specific. For that, we can use a default member initializer:

class Derived: public Base {
public:
    using Base::Base;
private:
    bool c = true;
};

Constructing a Derived(42) will invoke the Base(int) constructor, but also initialize c to true.

smac89
  • 39,374
  • 15
  • 132
  • 179
Barry
  • 286,269
  • 29
  • 621
  • 977
  • What if the value of `c` depends on one of the other fields in `Base`. Are those fields guaranteed to be initialized before this default initialization occurs? – smac89 Oct 08 '18 at 16:52
  • @smac89 Yes, initialization order of subobjects in C++ is _always_ declaration order. – Barry Oct 08 '18 at 16:52
  • Perfect. That was what I was really after. – smac89 Oct 08 '18 at 16:53