c++ - 使用虚拟继承时初始化基类

标签 c++ constructor xcode5 virtual-inheritance ctor-initializer

在 OS X 上使用 Xcode 5.1 编译以下代码时遇到意外错误。 Apple LLVM 版本 5.1 (clang-503.0.40)(基于 LLVM 3.4svn)

class GrandParent
{
public:
    GrandParent(int age) : m_age(age)
    {
    }

    virtual ~GrandParent() {}

private:
    GrandParent();
    GrandParent(const GrandParent&);
    const GrandParent& operator=(const GrandParent&);

    int m_age;
};

class Parent1 : public virtual GrandParent
{
public:
    Parent1(int age) : m_age(age)
    {
    }

    virtual ~Parent1() {}

private:
    Parent1();
    Parent1(const Parent1&);
    const Parent1& operator=(const Parent1&);

    int m_age;
};

class Parent2 : public virtual GrandParent
{
public:
    Parent2(int age) : m_age(age)
    {
    }

    virtual ~Parent2() {}

private:
    Parent2();
    Parent2(const Parent2&);
    const Parent2& operator=(const Parent2&);

    int m_age;
};

class Child : public Parent1, public Parent2
{
public:
    Child(int grandParentAge, int parent1Age, int parent2Age, int childAge) :
        GrandParent(grandParentAge),
        Parent1(parent1Age),
        Parent2(parent2Age),
        m_age(childAge)
    {
    }

    virtual ~Child() {}

private:
    Child();
    Child(const Child&);
    const Child& operator=(const Child&);

    int m_age;
};

报告的错误是:

error: inherited virtual base class 'GrandParent' has private default constructor
    Parent1(int age) : m_age(age)
    ^
note: declared private here
    GrandParent();
    ^
error: inherited virtual base class 'GrandParent' has private default constructor
    Parent2(int age) : m_age(age)
    ^
note: declared private here
    GrandParent();

我的理解是,虚拟基类(GrandParent)的构造函数不会被继承它的类(Parent1或Parent2)调用。相反,构造函数由具体类(Child)的构造函数调用。

这是正确的吗?

如果我为 GrandParent 提供默认构造函数,它可以编译正常。但是,如果我构造一个子对象:

Child child(80, 50, 49, 20);

检查一下我可以看到:

Child) child = {
  Parent1 = {
    GrandParent = (m_age = 49)
    m_age = 50
  }
  Parent2 = {
    GrandParent = (m_age = 80)
    m_age = 49
  }
  m_age = 20

因此,使用 Parent1 时祖 parent 的年龄不正确,但使用 Parent2 时祖 parent 的年龄是正确的。

我是不是误会了什么?或者该错误可能是编译器错误?

更新

如果我更新 Parent1 的 ctor(并对 Parent2 执行相同操作):

Parent1(int 年龄) : GrandParent(100), m_age(年龄) { }

现在可以编译了。检查这些值可以发现:

(Child) child = {
  Parent1 = {
    GrandParent = (m_age = 49)
    m_age = 50
  }
  Parent2 = {
    GrandParent = (m_age = 80)
    m_age = 49
  }
  m_age = 20

这显然是不对的。此外,修改后的代码使用VS 2013 Express在Windows上编译,检查值正确。

最佳答案

所有定义的角色,无论是否默认,都必须有效。

虽然除了最派生的构造函数之外,在运行时都会跳过虚拟基的初始化,但它仍然必须有效。

引自 C++14 最终草案 (n3936):

12.6.2 Initializing bases and members [class.base.init]

7 The expression-list or braced-init-list in a mem-initializer is used to initialize the designated subobject (or, in the case of a delegating constructor, the complete class object) according to the initialization rules of 8.5 for direct-initialization.
[ example omitted ]
The initialization performed by each mem-initializer constitutes a full-expression. Any expression in a mem-initializer is evaluated as part of the full-expression that performs the initialization. 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.
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 ctor-initializer), then

  • 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
    • 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;
  • otherwise, if the entity is an anonymous union or a variant member (9.5), no initialization is performed;
  • otherwise, the entity is default-initialized (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. —end note ]

我特别建议您注意最后一条注释,您可能会用它作为理由。
问题是,注释是非规范性的,并且该注释与其前面的规范性文本完全矛盾。

似乎 clang++-3.5.0 将此注释视为福音,而 g++-4.9.0 则没有:
http://coliru.stacked-crooked.com/a/ded8d46cc29ac79f

关于c++ - 使用虚拟继承时初始化基类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26098900/

相关文章:

c++ - 复制初始化和显式构造函数 - 编译器差异

ios - Xcode 5 布局错误 - 错位的 Label View Frame 在运行时会有所不同

ios - Xcode 5.0 的 Xamarin 4.0.10 不会在设备上运行,但会在模拟器中运行

iphone - iOS 7 中导航 Controller 及其 Root View Controller 的奇怪行为

c++ - 是否可以在不复制的情况下向下转换shared_ptr?

c++ - 创建动态字符串数组时出现段错误

c++ - 何时在 C++ 中返回指针、标量和引用?

c++ - C++类的大小是如何确定的?

kotlin - 您如何在Kotlin中进行合格的父类(super class)构造函数调用?

c# - 通用构造函数和继承