1

I was happy to find out that in C++11 we can inherit constructors like:

class Foo
{public:
    Foo(int a, double b, std::string name, char somethingElse);
};

class Derived : public Foo
{public:
    using Foo::Foo;
};

But I'm finding that I'm often extending the base class where there might be maybe one or two extra features, and I need to initialise a couple extra members by maybe passing as extra argument or something. In this case is seems I have to rewrite the constructor and pass all the arguments to the base one. I'm wondering if there is a better solution. I thought maybe just using the inherited constructor and then initialising the extra member on the next line after construction, but it doesn't seem right:

Derived d = Derived(6, 6.0, "name", 'a');
d.extraMember = 4;
d.funcptr = &somefunction;

I figured it was an excellent feature but then I realised more and more that my extended classes needed extra initialisation info.

Here's my example from my code:

struct Window
{
    Window(Window* parent, vec2 position, vec2 size, const String& name);
};
struct ConsoleWindow : Window
{
    using Window::Window;
    // But I've had to rewrite the constructor again because in this constructor I do stuff like 
    //GUI::bIsConsoleWindowActive = true;
    //GUI::unselectWindow();
    //GUI::selectedWindow = this;

}

It seems to me you can't add extra things to the construction process without rewriting the constructor and calling the base and passing all the values. This is common throughout my classes.

Zebrafish
  • 11,682
  • 3
  • 43
  • 119
  • 2
    How are you going to initialize an object that needs six variable parameters without supplying a constructor accepting six variables? Can you provide a code example of what you are hoping for? – Galik Jan 27 '18 at 22:20
  • @Galik I've added an example from my code in case it's more understandable where my problem is. – Zebrafish Jan 27 '18 at 22:31
  • So what do you want to be able to do *instead* of rewriting the constructor? – Galik Jan 27 '18 at 22:34
  • @Galik Well I don't know what the language is capable of, at first glance my options seem to rewrite the constructor, which I seem to be doing often with each new class, or using a couple of extra lines to initialise the extra members after constructing the object, or I dunno, I've thought of an initialise() function. Storyteller's method isn't a bad way. – Zebrafish Jan 27 '18 at 22:37

4 Answers4

4

If your base classes are copyable/moveable and not abstract, you can push the burden of constructing the base class onto the declaration of the derived object. Just accept a base object:

Derived(Base b, int extraMember) :
  Base(std::move(b)), extraMember(extraMember)
{
}

Where the declaration becomes this

Derived d{Base{6, 6.0, "name", 'a'}, 4};

That isn't perfect either, since it places certain requirements on your base classes that may not hold. And if it looks like aggregate initialization to you, then that's because this sets out to mimic it.

I'm afraid the only way that can definitely be adapted to any situation, is to spell out a new c'tor like you do now.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • That's actually not a bad way. I'm guessing this inheriting of constructors they've had since C++11 is only of any use if your derived classes only override functions for example, because I'm constantly finding myself rewriting them. Oh well. – Zebrafish Jan 27 '18 at 22:35
  • @Zebrafish - Not just overriding functions. Sometimes the member data we add is only ever default initialized, so any c'tor is only for supplying arguments to the base class. In this case, inheriting makes sense also. – StoryTeller - Unslander Monica Jan 27 '18 at 22:37
  • I suppose the new in-class initialisers help too. – Zebrafish Jan 27 '18 at 22:41
  • @Zebrafish - They do indeed. On the whole, I'd say it's easier to define how a class is initialized in C++11 than before. It's still not perfect however. – StoryTeller - Unslander Monica Jan 27 '18 at 22:44
1

It wasn't clear from your question if you are able to change the base class. Assuming you are, you might consider packaging up your base constructor arguments into a struct, which could be forwarded up from the derived constructor.

This is often seen when implementing the builder design pattern, which is one way to solve the telescoping constructor anti-pattern you're describing.

struct FooParts {
    int i;
    double d;
    string s;
    char c;
};

class Foo {
public:
    Foo(FooParts const & parts) : parts(parts) {}
private:
    FooParts parts;
};

class Derived : public Foo {
public:
    Derived(FooParts const & parts, bool extra) : Base(parts), member(extra) {}
private:
    bool member;
};
yoyoyango
  • 140
  • 7
0

I'm not sure, from your question, if you realize that you can call the base constructor from your derived constructor. Using your example:

struct Window
{
   Window(Window* parent, vec2 position, vec2 size, const String& name);
};
struct ConsoleWindow : Window
{
   ConsoleWindow(Window* parent, vec2 position, vec2 size, const String& name, int otherValue)
   : Window(parent, position, size, name)
   {
      // do something with `otherValue` here. 
   }
};

See for example here: calling the base class constructor in the derived class constructor

Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
  • Yes that's how I've been doing it. As usually each new class of mine has something different down in the constructor I can't really use the new c++11 constructor inheritance because it has to be identical – Zebrafish Jan 27 '18 at 23:28
  • @Zebrafish I guess I just don't understand where the difficulty is. You don't like enumerating all parameters to the base constructor? Can't be that much work, can it? Copy-paste? Just don't change the interface to your base class! :) – Cris Luengo Jan 27 '18 at 23:44
0

The most straightforward way to do what you described is to use the base class constructor in the initialization list of the derived class:

class Foo
{public:
    Foo(int a, double b, std::string name, char somethingElse);
};

class Derived : public Foo
{
public:
    Derived::Derived(int a, double b, std::string name, char somethingElse, 
                     int _extraMember, char derivedSpecificThing) :
        // Initialization list starts here
        Foo(a, b, name, somethingElse),
        extraMember(_extraMember)
    {
        // Do something with derivedSpecificThing etc.
    }

private:
    const int extraMember;
};

using Foo::Foo only exposes the constructor of the base class as an additional constructor for your derived class. Of course, the constructor of the base class cannot initialize the additional members of the derived class that it knows nothing about.