在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时GrandParent的年龄不正确,但对于Parent2,它是正确的。
我误解了什么吗?或者错误可能是编译器错误?
更新
如果我将Parent1的ctor(并对Parent2执行相同操作)更新为:
Parent1(int age):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上编译,检查值正确。
答案 0 :(得分:1)
当程序执行行
时 Parent1(int age) : m_age(age)
它尝试创建一个Parent1对象,它是一个类型为GrandParent的派生类。从Parent1类的构造函数定义可以看出,它只是通过调用它的默认构造函数来尝试创建一个GrandParent对象。
Parent1(int age) : m_age(age) // Here the default constructor of GrandParent is called implicitly.
{
}
但是,由于您已将GranParent类的默认构造函数定义为private,因此compıler会给出该错误。
答案 1 :(得分:1)
所有定义的cors,默认或不默认,必须有效。
虽然除了派生最多的ctor之外,在运行时跳过虚拟基础的初始化,但它必须仍然有效。
引自C ++ 14最终草案(n3936):
12.6.2初始化基数和成员
[class.base.init]
7 mem-initializer 中的表达式列表或 braced-init-list 用于初始化指定的子对象(或者,在委托构造函数的情况下,完整的类对象)根据8.5的初始化规则进行直接初始化 [例子省略]
每个 mem-initializer 执行的初始化构成一个完整表达式。 mem-initializer 中的任何表达式都将作为执行初始化的完整表达式的一部分进行计算。 mem-initializer ,其中 mem-initializer-id 表示虚拟基类在执行任何不是派生类最多的类的构造函数时被忽略强>
8在非委托构造函数中,如果给定的可能构造的子对象未由 meminitializer-id 指定(包括没有 mem-initializer-list 的情况,因为构造函数没有 ctor-initializer ),然后
- 如果实体是具有大括号或等于初始化程序的非静态数据成员,则
- 构造函数的类是一个union(9.5),该联合的其他变体成员没有被 mem-initializer-id 或
指定- 构造函数的类不是联合,并且,如果实体是匿名联合的成员,则 mem-initializer-id 不会指定该联合的其他成员, 按照8.5;
中的规定初始化实体- 否则,如果实体是匿名联合或变体成员(9.5),则不执行初始化;
- 否则,实体默认初始化(8.5)。
[注意:抽象类(10.4)永远不是最派生的类,因此它的构造函数永远不会初始化虚拟基类,因此可以省略相应的mem-initializer。 - 后注]
我特别赞扬你注意到的最后一个注意事项,你可能会将其用作理由 麻烦的是,笔记是非规范性的,而且这个笔记与其前面的规范性文本完全相矛盾。
似乎clang ++ - 3.5.0将音符作为福音,而g ++ - 4.9.0则没有: