【问题标题】:Calling constructor of non-template base class through template class constructor in initialization list通过初始化列表中的模板类构造函数调用非模板基类的构造函数
【发布时间】:2016-11-04 21:11:48
【问题描述】:

我在类层次结构中遇到了一个到目前为止我无法解决的问题。下面是一个最小示例,其中模板Base 类本身继承自另一个不是模板的类(在下面的示例中称为AbsoluteBase)。然后类Derived 继承自Base,其模板参数也是Bases' 模板参数:

#include <iostream> 
using namespace std;

class AbsolutBase {
    protected:
        int number;

        AbsoluteBase(int _number) {
            number = _number;
        }
        virtual ~AbsoluteBase() {}
        virtual void print() const = 0;
};

template <typename T> class Base : virtual public AbsoluteBase {
    public:
        Base(int _number) : AbsoluteBase(_number) {}

        virtual void print() const {
            cout << number << endl;
        }
};

template <typename T> class Derived : public Base<T> {
    public:
       Derived(int _number) : Base<T>::Base(_number) {}
};

int main() {
    Derived<char> object(100);
    object.print();
}

因此,每个构造函数都会调用其父构造函数,并将整数作为参数一直传递到AbsoluteBase。但是在编译我得到的代码时:

error: no matching function for call to 'AbsoluteBase::AbsoluteBase()'
note: candidates are: AbsoluteBase::AbsoluteBase(int)
note: candidate expects 1 argument, 0 provided

创建Base 的实例可以正常工作,但是当在Derived 的初始化列表中调用其构造函数时,编译器希望AbsolutBase() 作为构造函数,即使给出了整数参数。显然,在AbsoluteBase 中定义默认构造函数时,print() 函数会输出垃圾,因为没有向number 传递任何值。

所以我对Base&lt;T&gt;::Base(int) 的调用肯定有问题,但我看不出它是什么。我很感激每一个解释和帮助!

您好, 本尼切克

【问题讨论】:

    标签: c++ templates inheritance constructor


    【解决方案1】:

    AbsoluteBase 是一个虚拟基类。因此,它必须由最派生类的构造函数初始化。您的初始化程序 AbsoluteBase(_number) 是有效的,但仅当您直接实例化 Base&lt;T&gt; 类型的对象时才使用它。

    最好的解决方案可能是不让AbsoluteBase 成为虚拟基类。

    这条规则的原因是:

    class Silly: public Base<int>, Base<long>
    {
    public:  
        Silly() : Base<int>::Base(1), Base<long>::Base(2) {}
    };
    

    只有一个AbsoluteDerived 对象(这就是virtual 在此上下文中的含义),那么它是用1 还是2 初始化的?

    【讨论】:

      【解决方案2】:

      因为虚拟继承。当base虚拟地从另一个类继承时,base 的子类也必须调用其父类的虚拟父类的构造函数。由于您没有这样做,编译器正在尝试调用无参数 AbsoluteBase 的 ctor。所以你只需要编写如下代码:

      template <typename T> class Derived : public Base<T> {
          public:
             Derived(int _number) : AbsoluteBase(_number), Base<T>::Base(_number) {}
      };
      

      【讨论】:

      • 非常感谢,我不知道虚拟继承这个规则。
      【解决方案3】:

      您将 Absolute Base 声明为 class AbsolutBase 而没有 e。修正错字后编译就好了。

      编辑:如果您的类库从 Absolute 进行虚拟继承,它也不会编译。除非你需要虚拟继承(使用多重继承?)你可以声明它类Base : public AbsoluteBase

      如果您确实需要虚拟继承(菱形问题),您需要在 Derived 类中初始化 AbsoluteBase。鉴于 Base 虚拟继承自 AbsoluteBase,Derived 也可以继承自 Base2,后者也虚拟继承自 AbsoluteBase(这是虚拟继承的重点,一个类可以从两个不同的类继承,而这两个类本身继承自一个公共基类)。由于是虚继承,Base和Base2可以继承自AbsoluteBase,但AbsoluteBase只能有1个,那么它是如何初始化的呢?这就是为什么需要 Derived 直接初始化它,这样当 AbsoluteBase 的 1 份拷贝出来的时候,就很好理解如何初始化了。

      同样,这很麻烦,如果您不需要虚拟继承(我猜您可能不需要...),您可以公开让 Base 从 AbsoluteBase 继承并收工。

      【讨论】:

      • 现在我因为错字而感到愚蠢......但它不在我的编译代码中,只是在我上面的帖子中。关于虚拟继承:啊,很好,我不知道……我使用了虚拟继承,因为在进一步的代码中可能会出现菱形问题,但不知道在最派生类中初始化的这条规则。谢谢!
      猜你喜欢
      • 2021-04-23
      • 1970-01-01
      • 1970-01-01
      • 2016-07-02
      • 2019-03-14
      • 2013-06-06
      • 1970-01-01
      • 2017-09-12
      • 1970-01-01
      相关资源
      最近更新 更多