Search code examples
c++constructorinitializationsuperclassclass-variables

Workaround for inherited constructor and additional variables


I need a workaround or a nice solution for initializing a bunch of constant class variables in base and subclass

The problem is simple, I got a baseclass with two constructor and the same two constructor in the subclass

class BASE {
    int a;

    int func(float x) { // just a dummy to show that this function
        return (a = (int) x) + 2; // modifies a
    }
public:
    const int b;

    BASE(const int b) : b(b) {} // doesn't use a
    BASE(const float x) : a(0), b(func(x)) {}
};

struct SUB: public BASE {
    const int c;
    const int d;

    SUB(const int b) : BASE(b), c(b), d(c + 3) {}
    SUB(const float x) : BASE(x), c(b), d(c + 3) {}
};

The subclass needs to call the constructor from BASE to initialize the class variables from BASE after that the sub class initialize the remaining variables

So far so good but the problem is that both constructor from SUB do the exactly same except calling a different constructor from BASE

I want something like that

    SUB() : c(b), d(c + 3) {} // BASE() needs to be defined
    SUB(const int b) : BASE(b), SUB() {}
    SUB(const float x) : BASE(x), SUB() {}

but that doesn't work because "a call to a delegating constructor shall be the only member-initializer" ...

Moving everything outside the initializer list doesn't work because these are const class variables


Solution

  • You can create a "forwarding constructor" for your derived class:

    struct SUB: public BASE {
        const int c;
        const int d;
    
        template <class... T>
        SUB(T&&... a) : BASE(std::forward<T>(a)...), c(b), d(c + 3) {}
    };
    

    This will accept arbitrary arguments and forward them on to BASE. Of course, it will only compile when called with arguments which are valid for BASE, but that holds for every case.

    If you want/need to be super-correct, you can condition the constructor using SFINAE on somethin like std::is_constructible<BASE, T&&...>, but I wouldn't bother.