为什么C ++ 11中的统一初始化在虚拟基类中表现得很奇怪?

时间:2016-06-27 19:29:20

标签: c++ c++11 inheritance multiple-inheritance virtual-inheritance

现在,我正在学习C ++中的继承功能,并想测试最近学到的虚拟基类的概念。 我尝试了以下简单的代码:

#include <iostream>

using namespace std;

class A
{
private:
    int m_value;
    string m_caller;
public:
    A(int p_value, string p_caller) : m_value{p_value}, m_caller{p_caller}
    {
        cout<<"Instantiating A via "<<m_caller<<endl;
    }
};

class B : virtual public A
{
private:
    int m_value;
public:
    B(int p_value1,int p_value2) : A{p_value1,"B"}, m_value{p_value2}
    {
        cout<<"Instantiating B."<<endl;
    }
};

class C : public B
{
public:
    C(int p_value1,int p_value2) : A{p_value1,"C"}, B(p_value1, p_value2)
    {
        cout<<"Instantiating C."<<endl;
    }
};

int main()
{
    C c1(1,2);
    return 0;
}

请注意C类构造函数中的B(p_value1, p_value2)。这给了我想要的输出:

Instantiating A via C
Instantiating B.
Instantiating C.

但是,当我将其更改为B{p_value1, p_value2}时,我得到以下输出:

Instantiating A via C
Instantiating A via B
Instantiating B.
Instantiating C.

我试着寻找答案,但我得到的所有答案都引用了一些C ++标准。作为OOP的初学者,我正在寻找一个更简单的解释这种行为。 非常感谢!

P.S。我在Windows中使用C :: B,编译器为g ++ 4.8.1。

1 个答案:

答案 0 :(得分:8)

这是g ++中的编译器错误。

在C ++ 14(N4140)部分[dcl.init.list]中,列表初始化的定义是(为简洁而编辑):

  

类型T的对象或引用的列表初始化定义如下:

     
      
  • 如果T是聚合,则执行聚合初始化
  •   
  • 否则,如果初始化列表没有元素且T是具有默认构造函数的类类型,则该对象是值初始化的。
  •   
  • 否则,如果T是std :: initializer_list的特化,[...]
  •   
  • 否则,如果T是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载解析选择最佳构造函数。如果转换任何参数需要缩小转换,则程序格式不正确。
  •   
  • [...]
  •   

前3个点不适用:B不是聚合(聚合不能有基类),初始化列表确实有元素,B不是std::initializer_list的特化1}}。

第四点确实适用,因为根据[over.match.list] /1.2,重载决议将B{p_value1, p_value2}与构造函数B(int, int)匹配:

  

如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类T的所有构造函数,参数列表由初始化列表的元素组成。

从最后一句话开始,B(whatever)B{whatever}的行为应该相同。