vincent vincent - 1 year ago 69
C++ Question

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?

Answer Source

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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download