4

Note: Not a duplicate of C++ 11 Delegated Constructor Pure Virtual Method & Function Calls -- Dangers?. This other question refers to a conceptually similar problem that doesn't really present a solution for this case.

Consider the following program:

#include <iostream>
using std::cout;
using std::endl;

class Base {
    virtual void init() = 0; // a hook function
  public:
    Base(int a, int b) { /* ... */  init(); }
    Base(char a, int b) { /* ... */  init(); }
    Base(char a, int b, double* c) { /* ... */  init(); }
    /* etc.  Dozens of constructors */
};    

class Derived1 : public Base {    
    void init() { cout << "In Derived1::init()" << endl; }    
  public:    
    using Base::Base;    
};      

class Derived2 : public Base {    
    void init() { cout << "In Derived2::init()" << endl; }    
  public:    
    using Base::Base;    
};    

int main() {
  Derived1 d1(1, 2);
  Derived2 d2('a', 3);
  return 0;
}

This code obviously does not run (though it does compile with warnings on some compilers). The question is, what is the best way to implement this sort of pattern? Assuming there are dozens of derived classes and dozens of constructors in the Base, reimplementing the Base constructors in the derived class (with calls to base constructors and init() in the body of the derived constructor) is not really ideal.

Community
  • 1
  • 1
Daisy Sophia Hollman
  • 6,046
  • 6
  • 24
  • 35
  • 1
    I'd recommend removing many of the newlines and empty comments so it's easier to comprehend the full code in the comparable small browser window. – dornhege Jan 14 '14 at 18:05

3 Answers3

2

That code needs to go in the derived class constructor, it cannot be run until the object has derived type.

But you can add it to all constructors at once using perfect forwarding:

class Derived1 : public Base
{
public:
    template<typename... T>
    explicit Derived1(T&&... t) : Base(std::forward<T>(t)...) {
      std::cout << "Derived1::init logic goes here" << endl;
    }
};
Cassio Neri
  • 19,583
  • 7
  • 46
  • 68
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • @DavidHollman: Yes and no. A template constructor is never the copy or move constructor, so pass-by-value won't use the template. But when you directly call a constructor, you might get the undesired one. See http://akrzemi1.wordpress.com/2013/10/10/too-perfect-forwarding/ – Ben Voigt Jan 14 '14 at 18:15
  • (reposting so that your comment will make sense) Doesn't this overload the default copy and move constructors? – Daisy Sophia Hollman Jan 14 '14 at 18:17
1

Another approach is a 'named' constructor:

#include <iostream>

class Base
{
    public:
    template <typename Derived>
    static Derived construct() {
        Derived derived;
        derived.hello();
        return derived;
    }

    protected:
    Base() {};

    public:
    virtual ~Base() {};

    public:
    virtual void hello() = 0;
};

class Derived : public Base
{
    public:
    virtual void hello() {
        std::cout << "Hello\n";
    }
};

int main ()
{
    Derived d = Base::construct<Derived>();
}
0

Make special factory for those classes, that call init() after object creation

sliser
  • 1,645
  • 11
  • 15