I'm sorry for the obscure title, not sure how to word it better.
Consider following inheritance hierarchy:
struct A
{
A(int) {}
};
struct B : virtual A
{
B() : A(42) {}
};
struct C : B
{
C() : A(43) {}
};
It does work. Now let's say I want to create a template that can be transparently injected in the middle of a hierarchy, like this:
template <typename ...P>
struct Proxy : P...
{
// This constructor doesn't change anything. It was added
// to indicate that `Proxy` should only be used as a base.
protected:
Proxy() = default;
};
struct D : Proxy<B>
{
D() : A(44) {}
};
This gives me: error: call to implicitly-deleted default constructor of 'Proxy<B>'
.
Run on gcc.godbolt.org
I understand what's going on: Proxy
can't have a default constructor, because it doesn't provide an initializer for A
, hence the derived class can't default-construct Proxy
.
But it doesn't make sense if you think about it, because even if I provided an initializer for A
in Proxy
, D
would ignore it and would have to provide its own.
How can I work around this limitation?
Everything in code can be changed, but I'd prefer less invasive changes.
In my actual code, there's only one base class that causes those problems, so I made two different default constructors for Proxy
(distinguished with requires
): one doing nothing, and the other (which is used when any of P...
virtually inherits from A
) passing a dummy parameter to A::A(int)
.
But I don't like this solution because it's not generic, so I'm looking for better alternatives.
[special]/7:
For a class, its non-static data members, its non-virtual direct base classes, and, if the class is not abstract ([class.abstract]), its virtual base classes are called its potentially constructed subobjects.
[class.default.ctor]/2.7 says that a defaulted default constructor is defined as deleted if
any potentially constructed subobject, except for a non-static data member with a brace-or-equal-initializer, has class type
M
(or array thereof) and eitherM
has no default constructor or overload resolution ([over.match]) as applied to findM
's corresponding constructor results in an ambiguity or in a function that is deleted or inaccessible from the defaulted default constructor
So we can exclude virtual bases from the set of potentially constructed subobjects by making Proxy
abstract, for instance by making the destructor pure virtual (but still supply a definition):
template <typename ...P>
struct Proxy : P...
{
protected:
virtual ~Proxy() = 0;
Proxy() = default;
};
template <typename ...P>
Proxy<P...>::~Proxy() = default;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With