Mixin 构造函数嵌套包扩展

时间:2021-04-23 15:02:29

标签: c++ inheritance variadic-templates virtual mixins

我正在尝试实现一个 Mixin 类,但遇到了一些问题:

这是我目前所拥有的:

#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;
}

我希望使用 MixinVisitor 构造函数中指定的参数调用所有类。 Der1Der2 实际上继承自同一个 Base 类。

我现在的代码可以用 Clang 编译,但用 GCC 编译失败。 不过,Clang 的行为并不是我所期望的,因为我认为输出只有被调用的默认构造函数。

gcc 错误如下:

prog.cc:44:68: error: invalid use of pack expansion expression
   44 |   MixinVisitor(Args&&... args) : Mixin(std::forward<Args>(args)...)...
      |                                                                    ^~~
1

此外,输出不是我所期望的,这可能是由于虚拟继承。

我只看到调用的默认构造函数:

default ctor
4204112

如何强制调用正确的构造函数?

编辑: 感谢评论,gcc 问题有一个解决方法:

  MixinVisitor(Args&&... args) : Mixin{std::forward<Args>(args)...}...

但是我仍然想知道为什么没有调用正确的构造函数

1 个答案:

答案 0 :(得分:1)

GCC 错误 invalid use of pack expansion expression 是一个错误 #88580。似乎是 GCC 10 中的回归。

如评论中所述,将 :Mixin(std::forward<Args>(args)...)... 更改为 :Mixin{std::forward<Args>(args)...}... 可以解决问题。

关于为什么调用默认的Base构造函数:虚基类的构造函数被最派生类的构造函数直接调用。在这种情况下,MixinVisitor 直接调用了 Base 构造函数,并且由于您没有提到要调用哪个,因此调用了默认的构造函数。这就是虚拟继承的工作原理(另请参阅 FAQ)。

如果您想改为调用 Base(int i, string s),请明确指定,例如像这样:

template<class... Mixin>
class MixinVisitor : public Mixin... {
public:
  template <typename... Args>
  MixinVisitor(Args&&... args)
      : Base(std::forward<Args>(args)...), Mixin{std::forward<Args>(args)...}...
  {
  }

  . . .

我不知道您的具体用例,但虚拟基类通常是带有默认构造函数的非常基本的样板类,以免不必要地限制使用。这样,mixin 就可以拥有不依赖于 base 的 ctors。

相关问题