15

I have a class hierarchy which boils down to

class Module { };

struct Port {
    Module& owner;
    Port(Module& owner) : owner(owner) {}
};

struct InPort    : virtual Port    { using Port::Port; };
struct OutPort   : virtual Port    { using Port::Port; };
struct InOutPort : InPort, OutPort { using Port::Port; };

As you can see, I would prefer to create some base functionality, and inherit it in a classic diamond pattern. I also would like to use constructor inheritance to make it as future proof as possible...

However, this does not work as written down above

prog.cpp: In function 'int main()':
prog.cpp:14:15: error: use of deleted function 'InOutPort::InOutPort(Module&)'
  InOutPort p(m);

Even replacing the definition of InOutPort with a more explicit version is not enough:

struct InOutPort : InPort, OutPort { InOutPort(Module& m) : Port(m), InPort(m), OutPort(m) { } };

Instead I seem to have to write down everything explicitly for it to work::

struct InPort    : virtual Port    { InPort(Module& m) : Port(m) { } };
struct OutPort   : virtual Port    { OutPort(Module& m) : Port(m) { } };
struct InOutPort : InPort, OutPort { InOutPort(Module& m) : Port(m), InPort(m), OutPort(m) { } };

Is there a way to combine constuctor inheritance with virtual inheritance that I am overlooking?
If not, what alternative would you use?
Maybe variadic template constructors that perfectly forwards its arguments to all bases?

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
danielschemmel
  • 10,885
  • 1
  • 36
  • 58
  • `InPort` and `OutPort` both inherit a constructor that calls the undeclared default-constructor for `Port` because they are both not a most-derived class. The program would be ill-formed if they were called. Because of this, gcc decides to delete the corresponding constructors in both classes. Clang doesn't & won't give an error about the constructors even when the bases are [initialized explicitly](http://coliru.stacked-crooked.com/a/7c2963ba56e79793). As an aside, clang also gives an error because `Port` isn't a direct base class of `InOutPort` in the using-declaration, while gcc ignores it. – David G Apr 15 '15 at 15:32
  • GCC does in fact delete the constructors because they would be ill-formed, but not for the reason I previously surmised. It seems the using-declaration [implicitly odr-uses the default-constructor](http://coliru.stacked-crooked.com/a/490c0b5bb17fdc50). The default-constructor in your `Port` class is not declared, so it deletes the calling constructor. This is a bug. – David G Apr 15 '15 at 16:40
  • In addition, it only happens if it's a virtual base class. – David G Apr 15 '15 at 16:49

1 Answers1

1

It doesn't appear that there is any way to do such a thing. In 12.9/8:

...An implicitly-defined inheriting constructor performs the set of initializations of the class that would be performed by a user-written inline constructor for that class with a mem-initializer-list whose only mem-initializer has a mem-initializer-id that names the base class denoted in the nested-name-specifier of the using-declaration and an expression-list as specified below...

In other words, the class whose constructor you inherit is the only base class that gets arguments forwarded to it. All other base classes need to have default constructors. Since you hid those default constructors in the intermediate classes by inheriting the parent constructor, you can't call them once you explicitly inherit the parent constructor.

I think you should be able to use inherited constructors for both intermediate classes and only write the explicit version for the most derived class [I didn't see that you already tried this - it does seem like a compiler bug from my understanding of the standard]. I'll update if I think of another better approach.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • That is pretty much what I figured after twisting my brain back into the course of virtual inheritance - but why does the middle version (explicit `InOutPort` with inheriting `InPort` and `OutPort´) not work? It seems like a g++ bug, doesn't it? – danielschemmel Apr 15 '15 at 16:02