简体   繁体   English

您是否需要从所有派生类调用虚拟基类构造函数? 即使他们不是最衍生的?

[英]Do you need to call virtual base class constructor from all derived classes? Even if they're not the most derived?

I am having trouble with multiple inheritance and the diamond problem. 我在多重继承和钻石问题上遇到了麻烦。

The problem occurs because my base class constructor requires a parameter. 出现此问题是因为我的基类构造函数需要一个参数。 The compiler tries to generate a default constructor for my two abstract classes, but that fails because the default constructor cannot determine the parameter for the base class. 编译器尝试为我的两个抽象类生成默认构造函数,但这会失败,因为默认构造函数无法确定基类的参数。

I don't understand why my abstract classes are calling the base constructor. 我不明白为什么我的抽象类调用基础构造函数。 I thought the most derived class is the one that calls the virtual base class constructor. 我认为最派生的类是调用虚基类构造函数的类。

Here is code that reproduces what I'm talking about: 这是重现我正在谈论的代码:

class VirtualBase
{

        public:

                VirtualBase(int initial) :
                        count(initial)
        {}

                int getCount() const
                {
                        return count;
                }

                void increment()
                {
                        count++;
                }

        private:

                int count;

};


class ContractA : public virtual VirtualBase
{

        public:

                virtual void doSomething() = 0;

};

class ContractB : public virtual VirtualBase
{

        public:

                virtual void doSomethingElse() = 0;

};

class Concrete : public ContractA, public ContractB
{

        public:

                Concrete() : 
                        VirtualBase(0)
        {}

                virtual void doSomething()
                {
                         increment();
                }

                virtual void doSomethingElse()
                {
                        // etc...       
                }

};

int main()
{

        Concrete concrete;
        concrete.doSomething();
        concrete.doSomethingElse();
        return 0;
}

I get the following error (for each Contract): 我收到以下错误(对于每个合同):

main.cpp: In constructor ‘ContractA::ContractA()’:
main.cpp:29:7: error: no matching function for call to ‘VirtualBase::VirtualBase()’
 class ContractA : public virtual VirtualBase
       ^
main.cpp:29:7: note: candidates are:
main.cpp:9:3: note: VirtualBase::VirtualBase(int)
   VirtualBase(int initial) :
   ^
main.cpp:9:3: note:   candidate expects 1 argument, 0 provided
main.cpp:4:7: note: VirtualBase::VirtualBase(const VirtualBase&)
 class VirtualBase
       ^
main.cpp:4:7: note:   candidate expects 1 argument, 0 provided
main.cpp: In constructor ‘Concrete::Concrete()’:
main.cpp:53:17: note: synthesized method ‘ContractA::ContractA()’ first required here 
    VirtualBase(0)
                 ^

Your example compiles with EDG and clang but it does not compile with gcc . 你的例子用EDGclang编译,但它不用gcc编译。 I'm not sure if the code should compile as is as it seems the constructor of the abstract base class is declared as deleted: According to 12.1 [class.ctor] paragraph 4, sixth bullet, the defaulted default constructor is declared as deleted if any subobject doesn't have a default constructor: 我不确定代码是否应该按原样编译,因为似乎抽象基类的构造函数被声明为已删除:根据12.1 [class.ctor]第4段,第6个子弹,默认的默认构造函数被声明为已删除任何子对象都没有默认构造函数:

... A defaulted default constructor for class X is defined as deleted if: ... ...如果出现以下情况,则将类X的默认默认构造函数定义为已删除:...

  • ... ...
  • any potentially constructed subobject, except for a non-static data member with a brace-or-equalinitializer, has class type M (or array thereof) and either M has no default constructor or overload resolution (13.3) as applied to M 's default constructor results in an ambiguity or in a function that is deleted or inaccessible from the defaulted default constructor, or 任何可能构造的子对象,除了具有大括号或等号初始化器的非静态数据成员,具有类型M (或其数组),并且M没有应用于M的默认值的默认构造函数或重载决策(13.3)构造函数导致歧义或在默认默认构造函数中删除或无法访问的函数中,或者
  • ... ...

There is no special exemption for classes with virtual bases with respect to creation of virtual bases, ie, the defaulted default constructor will be deleted. 没有为与类无特殊豁免virtual相对于创作基地virtual基地,即默认默认的构造函数将被删除。

For abstract classes it is apparently not necessary to call the virtual base from a constructors member initializer list. 对于抽象类,显然不必从构造函数成员初始化列表中调用虚拟基础。 At least, that is what 12.6.2 [class.base.init] paragraph 8 says according to its note: 至少,这就是12.6.2 [class.base.init]第8段根据其说明所说的:

In a non-delegating constructor, if a given potentially constructed subobject is not designated by a meminitializer-id (including the case where there is no mem-initializer-list because the constructor has no ctorinitializer), then 在非委托构造函数中,如果给定的可能构造的子对象不是由meminitializer-id指定的(包括没有mem-initializer-list的情况,因为构造函数没有ctorinitializer),那么

  • if the entity is a non-static data member that has a brace-or-equal-initializer and either 如果实体是具有大括号或等于初始化器的非静态数据成员
  • the constructor's class is a union (9.5), and no other variant member of that union is designated by a mem-initializer-id or 构造函数的类是一个union(9.5),并且该联合的其他变体成员没有由mem-initializer-id指定或
  • the constructor's class is not a union, and, if the entity is a member of an anonymous union, no other member of that union is designated by a mem-initializer-id, the entity is initialized as specified in 8.5; 构造函数的类不是联合,并且,如果实体是匿名联合的成员,则该联合的其他成员不是由mem-initializer-id指定的,该实体按照8.5中的规定进行初始化;
  • otherwise, if the entity is an anonymous union or a variant member (9.5), no initialization is performed; 否则,如果实体是匿名联合或变体成员(9.5),则不执行初始化;
  • otherwise, the entity is default-initialized (8.5). 否则,实体默认初始化(8.5)。

[ Note: An abstract class (10.4) is never a most derived class, thus its constructors never initialize virtual base classes, therefore the corresponding mem-initializers may be omitted. [注意:抽象类(10.4)永远不是最派生的类,因此它的构造函数永远不会初始化虚拟基类,因此可以省略相应的mem-initializers。 — end note ] ... - 结束说明] ......

The relevant part for the most derived base is in 12.6.2 paragraph 7, last sentence: 最衍生基础的相关部分在12.6.2第7段,最后一句:

... A mem-initializer where the mem-initializer-id denotes a virtual base class is ignored during execution of a constructor of any class that is not the most derived class. ...在执行不是最派生类的任何类的构造函数期间,将忽略mem-initializer,其中mem-initializer-id表示虚拟基类。

Unfortunately the C+03 language didn't make abstract classes a special case wrt. 不幸的是,C + 03语言并没有使抽象类成为特殊情况。 constructor invocations, and still as of C++11 and later the g++ compiler (my version 5.1.0) doesn't regard them as special for this. 构造函数调用,从C ++ 11及更高版本开始,g ++编译器(我的版本5.1.0)并不认为它们是特殊的。

And so in practice, for portable code, all bases must be initialized, as far as the compiler knows . 因此在实践中,对于可移植代码,必须初始化所有基础, 就编译器所知

If you want to make it clear to a reader of the source code that these are dummy calls, then you can do that via comments, or better, expressed directly in the source code with an assert : 如果你想向源代码的读者清楚这些是虚拟调用,那么你可以通过注释来做到这一点,或者更好,直接在源代码中用assert

#include <assert.h>

struct Dummy_call {};

class VirtualBase
{
private:
    int count;

protected:
    VirtualBase( Dummy_call ) { assert( false ); }

public:
    auto getCount() const -> int {
        return count;
    }
    void increment() {
        ++count;
    }

    VirtualBase( int const initial )
        : count(initial)
    {}
};

class ContractA
    : public virtual VirtualBase
{
public:
    virtual void doSomething() = 0;
    ContractA(): VirtualBase( Dummy_call{} ) {}
};

class ContractB
    : public virtual VirtualBase
{
public:
    virtual void doSomethingElse() = 0;
    ContractB(): VirtualBase( Dummy_call{} ) {}
};

class Concrete
    : public ContractA
    , public ContractB
{
public:
    void doSomething() override {
        increment();
    }
    void doSomethingElse() override {}   // etc...

    Concrete()
        : VirtualBase{ 0 }
    {}
};

int main()
{
    Concrete concrete;
    concrete.doSomething();
    concrete.doSomethingElse();
}

Your code is well-formed since C++11. 从C ++ 11开始,您的代码格式正确。 Unfortunately, you're seeing gcc bug 53878 . 不幸的是,你看到gcc bug 53878

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何调用所有基类的复制构造函数来复制C ++中钻石继承中的大多数派生类对象? - How to call copy constructor of all base classes for copying most derived class object in diamond inheritance in C++? 如何从基类构造函数调用派生类虚方法? - How to call derived class virtual method from base class constructor? 虚拟继承(钻石)-为什么需要从派生程度最高的类上转换为基类 - Virtual inheritance (diamond) - Why do I need to upcast to Base class from the most Derived class 即使派生类没有成员变量,我是否需要重新实现基类的所有构造函数? - Do I need to reimplement all the constructor of a base class even if a derived class has no member variables? 层次结构中的所有派生类是否都需要访问虚拟基类? - Do all derived classes from a hierarchy require access to the virtual base class? 为什么虚拟基类必须由最派生的类构造? - Why must virtual base classes be constructed by the most derived class? 派生类中的基本构造函数调用 - Base Constructor Call in Derived Class 为什么派生类的构造函数要在C++中初始化虚拟基类? - Why do the constructor of the derived classes want to initialize the virtual base class in C++? 派生类是否需要在基类上声明自己的虚函数版本? - Do derived classes need their own version of a virtual function declared on base? 从基础 class 构造函数调用派生 class 的虚拟 function? - Calling virtual function of derived class from base class constructor?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM