Virtual Inheritance with Constructor Inheritance - c++

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?

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.

Related

Inheriting constructors in C++20 (Visual Studio 2019)

I am using Visual Studio 2019 (v16.10.3) with /std:c++latest and this compiles:
class Base
{
public:
Base(int x) {}
};
class Derived : public Base
{
// no constructors declared in Derived
};
int main() {
Derived d(5);
}
For the previous versions of the standard I have to declare inherited constructors with the using directive:
class Derived : public Base
{
using Base::Base;
};
Is this something new that was put in C++20 or is it some Microsoft specific thing?
Is this something new that was put in C++20 or is it some Microsoft specific thing?
Not with relation to inherited constructors. What changed is that aggregate initialization may use parenthesis under certain conditions. Derived is considered an aggregate on account of having no private parts, so we initialize its bases and members directly.
It even works when we add a public member:
class Base
{
public:
Base(int ) {}
};
struct Derived : public Base
{
// no constructors declared in Derived
int y;
};
int main() {
Derived d(5, 4);
}
Live
This is the result of C++17 allowing classes with base classes to still be considered aggregates combined with C++20 rules allowing aggregate initialization to work with () constructor syntax if the values in the parentheses would work (and wouldn't call a constructor). So while it looks like a constructor call, it's actually aggregate initialization.
Derived doesn't have a constructor matching Base's constructor; you're merely initializing the aggregate Derived by providing a valid initializer for its subobject Base.

Two members, default initialized in Base, possibly non-default initialized in Derived

I have a base class with a default constructor.
class Base
{
public:
Base();
private:
Type1 m_ofType1;
Type2 m_ofType2;
}
Base::Base()
: m_ofType1(defaultExpr1)
, m_ofType2(defaultExpr2)
{
}
And I have a derived class, which may be:
default constructed, with m_ofType1 and m_ofType2 being initialized by their respective default expressions,
provided with a Type1 only, or
provided with a Type2 only.
class Derived : public base
{
public:
Derived(); // m_ofType1 and m_ofType2 will be initialized with their default expressions.
Derived(Type1 overrideOfType1); // m_ofType1 will be initialized with overrideOfType1 and m_ofType2 will be initialized with its default expression.
Derived(Type2 overrideOfType2); // m_ofType1 will be initialized with its default expression and m_ofType2 will be initialized with overrideOfType2.
}
Base is for Production code and Derived is for test code. I would like to implement the constructors without data or code duplication, but I can't see how.
Given the constraints, which I hope have been made clear, do you all know how to implement the above constructors without data or code duplication?
Example of code duplication:
We could add protected constructors to base:
Base(Type1);
Base(Type2);
That means that the Derived constructors would just forward the call to their respective Base constructors.
Derrived::Derrived()
: Base()
{
}
Derived::Derived(Type1 overrideOfType1)
: Base(overrideOfType1)
{
}
Derived::Derived(Type1 overrideOfType2)
: Base(overrideOfType2)
{
}
In doing this, the question changes a little. How do you implement the Base constructors without data or code duplication.
Here's my best attempt at doing this. It doesn't work, and I'll show you why.
First, add another constructor to Base:
Base(Type1 ofType1, Type2 ofType2);
Base::Base()
: Base(defaultExpr1, defaultExpr2)
{
}
Base::Base(Type1 overrideOfType1)
: Base(overrideOfType1, defaultExpr2)
{
}
Base::Base(Type2 overrideOfType2)
: Base(defaultExpr1, overrideOfType2)
{
}
Base::Base(Type1 ofType1, Type1 ofType2)
: m_ofType1(ofType1)
, m_ofType2(ofType2)
{
}
You can see that defaultExpr1 and defaultExpr2 have been duplicated.
I think the Derived is close to being a red herring. Basically you are asking for 3 ways to construct a Base and to do that it is Base that has to provide appropriate constructors, something along the line of:
struct Base {
Type1 m_ofType1{defaultExpr1};
Type2 m_ofType2{defaultExpr2};
Base() = default;
Base(Type1 x) : m_ofType1(x) {}
Base(Type2 x) : m_ofType2(x) {}
};
Default initializers (defaultExpr1 and defaultExpr2) can be overriden by initializers in the constructors initializer list.
Now add access restrictions and derived classes calling those constructor as you wish (perhaps making the last two protected and data members private). To stay with your example that would be
struct Derived : Base {
Derived(Type1 x) : Base(x) {}
Derived(Type2 x) : Base(x) {}
};
I doubt you can get it any shorter than this.
You can pass the arguments to private members 1 & 2 with a protected constructor. This will keep those members shielded without giving the derived classes full access to them.
class Base
{
private:
Base();
protected:
Base(Type1 overrideOfType1, Type2 overrideOfType2) { }
private:
Type1 m_ofType1;
Type2 m_ofType2;
};
class Derived : public Base
{
public:
Derived() : Base(Type1{}, Type2{}) { }
Derived(Type1 overrideOfType1) : Base(overrideOfType1, Type2{}) { }
Derived(Type2 overrideOfType2) : Base(Type1{}, overrideOfType2) { }
};
Oh, and by the way, members are initialized with the default constructor if they have one automatically, so you don't need to be explicit for the empty base constructor. For instance, if the default constructor of Base was accessible, you could just define:
Base::Base() = default;
And the members would have their default values.

Template hierarchy constructor arguments

I'm having trouble understanding why my template hierarchy won't pass constructor arguments deeper than one layer.
So far I've read that it may have something to do with namespacing or template constructors being unnamed. My various attempts at getting the constructor arguments through to the base class with scoping directives have failed.
// test.cc
#include <string>
// Base class, obviously.
template <typename T>
class Base {
public:
Base(const T& t) : _t(t) {}
virtual ~Base() { }
virtual T getVal() const { return _t; }
private:
T _t;
};
// First derivation. This works, although it is explicit.
class DerivedOne : public virtual Base<std::string>
{
public:
DerivedOne(const std::string& path) : Base<std::string>(path) {}
virtual ~DerivedOne() {}
};
// Second derivation. Constructor fails to compile unless I explicitly
// pass the 'path' argument to Base in the initializer list or create a
// default constructor for Base (which, of course, leaves Base::_t
// uninitialized).
class DerivedTwo : public virtual DerivedOne
{
public:
DerivedTwo(const std::string& path) : DerivedOne(path) {}
// This works
// DerivedTwo(const std::string& path) : DerivedOne(path), Base(path) {}
virtual ~DerivedTwo() {}
};
int main()
{
return 0;
}
The compiler complains:
test.cc: In constructor ‘DerivedTwo::DerivedTwo(const string&)’:
test.cc:31:58: error: no matching function for call to ‘Base<std::__cxx11::basic_string<char> >::Base()’
DerivedTwo(const std::string& path) : DerivedOne(path) {}
^
test.cc:7:5: note: candidate: Base<T>::Base(const T&) [with T = std::__cxx11::basic_string<char>]
Base(const T& t) : _t(t) {}
^
test.cc:7:5: note: candidate expects 1 argument, 0 provided
test.cc:5:7: note: candidate: Base<std::__cxx11::basic_string<char> >::Base(const Base<std::__cxx11::basic_string<char> >&)
class Base {
^
test.cc:5:7: note: candidate expects 1 argument, 0 provided
Why do I need to declare a default constructor when it seems that the parameterized constructor should be called? Why must I explicitly pass DerivedTwo's parameter to Base, since I've passed it to DerivedOne (who should pass it to Base)?
Is there some way to avoid this duplication? Does this mean I can't use initializer lists when a base template constructor parameter is allocated on the heap in a derived class initializer list?
What is going on
When you inherit virtually, you are asking that the type you inherit from is a "singleton" within the object heiarchy (not a program-wide singleton, but a instance-wide singleton, strange as that may seem).
As a side effect, the actual most derived class is responsible for constructing the virtual base class, regardless of what intermediate classes exist.
Virtual inheritance is intended when you need to inherit from some class more than once, yet ensure that only one instance exists. Rather than work out some complex set of rules to determine whose constructor call would be the one to be called, the designers of C++ mandated that the most-derived class (the actual one being created) dictates exactly what constructor is called.
Everyone else can chime in, but only if an actual instance of that specific class (and not a more-derived one) is created does their opinion matter.
You probably don't want virtual there
virtual inheritance has almost nothing to do with virtual methods/member functions. Don't use it unless you need to for binary layout or diamond inheritance reasons.
Simply eliminate it from your code, and if you never inherit from a class along 2 different paths to the same most-derived class, you won't miss it.
class DerivedOne : public Base<std::string>
and
class DerivedTwo : public DerivedOne
and your code compiles and does what you seem to want.
When you use virtual inheritance it is always the MOST DERIVED class that initializes the virtual base. Even though your intermediate class passes a value up, it won't do that in further derived classes, which must directly initialize Base.
Since your most derived class (DerivedTwo) doesn't directly initialize Base, then Base is initialized without any parameters passed to it, and so requires a default constructor.
class DerivedTwo : public virtual DerivedOne
{
public:
DerivedTwo(const std::string& path) : Base(path), DerivedOne(path) {}
}
Alternately, stop using virtual inheritance, and just use "normal" inheritance. That is, change this:
class DerivedOne : public virtual Base<std::string>
to this:
class DerivedOne : public Base<std::string>
Unless, of course, you really need virtual inheritance. :)

C++ no appropriate default constructor available - inherited templated constructor w/ no arguments

I've looked at over a dozen qst's with the phrase no appropriate default constructor available in it but none help with my own problem.
It's a very basic C++ qst (as I'm still learning the ropes), so sorry in advance if your eyes are glazing over at it's simplicity. I am trying to inherit a class that has a templated constructor with no arguments. Such as:
class Base {
public:
template<class T>
Base() {
T *t = this;
//more irrelevant stuff
}
}
I've tried
class Derived : public Base {
public:
Derived() : Base() {
}
}
and
class Derived : public Base {
public:
Derived() {
}
}
to no avail. In both cases I get the error message no appropriate default constructor available . How to go about doing this? If this is not possible can you explain why?
It worked when I set up my constructor like template<class T> Base(T *t) (taking the template argument as a parameter).
p.s. In case it matters, in my code I am also inheriting Derived by another class.
there is no syntax for accessing the default constructor in
class blah_t
{
public:
template< class other_t > blah_t() {}
};
there also some other ways to make the default constructor inaccessible, e.g.
class blah_t
{
public:
blah_t() {}
blah_t( int = 666 ) {}
};
as the holy standard explains it, constructors don't have names…
but, back to the original question, in order to be able to specify the template argument, you need some ordinary function argument that involves the template parameter type

How do I call the base class constructor?

Lately, I have done much programming in Java. There, you call the class you inherited from with super(). (You all probably know that.)
Now I have a class in C++, which has a default constructor which takes some arguments. Example:
class BaseClass {
public:
BaseClass(char *name); ....
If I inherit the class, it gives me the warning that there is no appropriate default constructor available. So, is there something like super() in C++, or do I have to define a function where I initialize all variables?
You do this in the initializer-list of the constructor of the subclass.
class Foo : public BaseClass {
public:
Foo() : BaseClass("asdf") {}
};
Base-class constructors that take arguments have to be called there before any members are initialized.
In the header file define a base class:
class BaseClass {
public:
BaseClass(params);
};
Then define a derived class as inheriting the BaseClass:
class DerivedClass : public BaseClass {
public:
DerivedClass(params);
};
In the source file define the BaseClass constructor:
BaseClass::BaseClass(params)
{
//Perform BaseClass initialization
}
By default the derived constructor only calls the default base constructor with no parameters; so in this example, the base class constructor is NOT called automatically when the derived constructor is called, but it can be achieved simply by adding the base class constructor syntax after a colon (:). Define a derived constructor that automatically calls its base constructor:
DerivedClass::DerivedClass(params) : BaseClass(params)
{
//This occurs AFTER BaseClass(params) is called first and can
//perform additional initialization for the derived class
}
The BaseClass constructor is called BEFORE the DerivedClass constructor, and the same/different parameters params may be forwarded to the base class if desired. This can be nested for deeper derived classes. The derived constructor must call EXACTLY ONE base constructor. The destructors are AUTOMATICALLY called in the REVERSE order that the constructors were called.
EDIT: There is an exception to this rule if you are inheriting from any virtual classes, typically to achieve multiple inheritance or diamond inheritance. Then you MUST explicitly call the base constructors of all virtual base classes and pass the parameters explicitly, otherwise it will only call their default constructors without any parameters. See: virtual inheritance - skipping constructors
You have to use initiailzers:
class DerivedClass : public BaseClass
{
public:
DerivedClass()
: BaseClass(<insert arguments here>)
{
}
};
This is also how you construct members of your class that don't have constructors (or that you want to initialize). Any members not mentioned will be default initialized. For example:
class DerivedClass : public BaseClass
{
public:
DerivedClass()
: BaseClass(<insert arguments here>)
, nc(<insert arguments here>)
//di will be default initialized.
{
}
private:
NeedsConstructor nc;
CanBeDefaultInit di;
};
The order the members are specified in is irrelevant (though the constructors must come first), but the order that they will be constructed in is in declaration order. So nc will always be constructed before di.
Regarding the alternative to super; you'd in most cases use use the base class either in the initialization list of the derived class, or using the Base::someData syntax when you are doing work elsewhere and the derived class redefines data members.
struct Base
{
Base(char* name) { }
virtual ~Base();
int d;
};
struct Derived : Base
{
Derived() : Base("someString") { }
int d;
void foo() { d = Base::d; }
};
Use the name of the base class in an initializer-list. The initializer-list appears after the constructor signature before the method body and can be used to initialize base classes and members.
class Base
{
public:
Base(char* name)
{
// ...
}
};
class Derived : Base
{
public:
Derived()
: Base("hello")
{
// ...
}
};
Or, a pattern used by some people is to define 'super' or 'base' yourself. Perhaps some of the people who favour this technique are Java developers who are moving to C++.
class Derived : Base
{
public:
typedef Base super;
Derived()
: super("hello")
{
// ...
}
};
There is no super() in C++. You have to call the Base Constructor explicitly by name.