Reputation: 4367
I am trying to implement a Mixin class, and facing some problems:
This is what I have at the moment:
#include <iostream>
#include <string>
using namespace std;
struct Base
{
Base()
{
cout << "default ctor" << endl;
}
Base(int i, string s)
:i_(i)
,s_(s)
{
cout << "i: " << i << " s:" << s << endl;
}
virtual ~Base() = default;
virtual void handle() {};
int i_;
string s_;
};
struct Der1 : virtual public Base
{
using Base::Base;
};
struct Der2 : virtual public Base
{
using Base::Base;
};
template<class... Mixin>
class MixinVisitor : public Mixin... {
public:
template <typename... Args>
MixinVisitor(Args&&... args) : Mixin(std::forward<Args>(args)...)...
{
}
};
int main()
{
MixinVisitor<Der1, Der2> m(10, "var");
cout << m.i_ << endl;
}
I want all the classes to be called with the parameters specified in the MixinVisitor
constructor.
Der1
and Der2
inherit virtually from the same Base class.
The code I have now compiles with Clang, but fails with GCC. The behavior with Clang is not what I expect though, as I see as output only the default constructor being called.
Gcc error is as follows:
prog.cc:44:68: error: invalid use of pack expansion expression
44 | MixinVisitor(Args&&... args) : Mixin(std::forward<Args>(args)...)...
| ^~~
1
Moreover the output is not what I am expecting, and that might be due to virtual inheritance.
I only see the default ctor being called:
default ctor
4204112
How can I force the proper constructor to be called?
edit: thank to the comments, there's a workaround for the gcc problem:
MixinVisitor(Args&&... args) : Mixin{std::forward<Args>(args)...}...
however I'd still like to know why the correct constructor is not called
Upvotes: 4
Views: 183
Reputation: 85530
The GCC error invalid use of pack expansion expression
is a bug #88580. Seems to be a regression in GCC 10.
As mentioned in the comments, changing :Mixin(std::forward<Args>(args)...)...
to :Mixin{std::forward<Args>(args)...}...
solves the issue.
Regarding why the default Base
constructor is called: the virtual base class's constructor is called directly by the most derived class's constructor. In this case, MixinVisitor
directly calls the Base
constructor, and since you don't mention which one to call, the default one is called. This is how virtual inheritance works (see also the FAQ).
If you want to call Base(int i, string s)
instead, specify it explicitly, e.g. like this:
template<class... Mixin>
class MixinVisitor : public Mixin... {
public:
template <typename... Args>
MixinVisitor(Args&&... args)
: Base(std::forward<Args>(args)...), Mixin{std::forward<Args>(args)...}...
{
}
. . .
I don't know about your specific use-case, but a virtual base is usually a very basic boilerplate class with a default ctor, so as not to restrict usage unnecessarily. This way the mixins can have ctors that do not depend on the base.
Upvotes: 2