C++中的虚拟继承和统一初始化

use*_*611 6 c++ inheritance gcc c++11 c++14

继续关于多个(虚拟)继承的这个问题,我想询问一个简单的MWE让g ++ 5.2.0沮丧,而clang ++ 3.6.2处理它就好了,没有任何抱怨,即使有-Wall-Wextra设置.所以这是MWE:

class Z {};
class A : virtual Z { protected: A() {} };
class B : virtual Z { protected: B() {} };
class C : A, B { public: C() : A{}, B{} {} };
int main() { C c{}; return 0; }
Run Code Online (Sandbox Code Playgroud)

与clang ++不同,g ++抱怨如下:

gccodd.c++: In constructor ‘C::C()’:
gccodd.c++:2:34: error: ‘A::A()’ is protected
 class A : virtual Z { protected: A() {} };
                                  ^
gccodd.c++:4:39: error: within this context
 class C : A, B { public: C() : A{}, B{} {} };
                                       ^
gccodd.c++:3:34: error: ‘B::B()’ is protected
 class B : virtual Z { protected: B() {} };
                                  ^
gccodd.c++:4:39: error: within this context
 class C : A, B { public: C() : A{}, B{} {} };
                                       ^
Run Code Online (Sandbox Code Playgroud)

使用旧表单替换C的构造函数中的统一初始化可以正常工作,但clang ++和g ++都对以下内容感到满意:

class C : A, B { public: C() : A(), B() {} };
Run Code Online (Sandbox Code Playgroud)

这产生了两个明显的选择:

  1. 代码以某种方式违反了标准,使得结果未定义(即,任何结果都是可接受的).
  2. 两个编译器中的一个有一个与统一初始化和多个+虚拟继承相关的错误.

如果是投票问题,(1)可能会赢,因为icpc 15.0.0说明如下:

gccodd.c++(4): error #453: protected function "A::A()" (declared at line 2) is not accessible through a "A" pointer or object
  class C : public virtual A, public virtual B { public: C() : A{}, B{} {} };
                                                                ^

gccodd.c++(4): error #453: protected function "B::B()" (declared at line 3) is not accessible through a "B" pointer or object
  class C : public virtual A, public virtual B { public: C() : A{}, B{} {} };
                                                                     ^
Run Code Online (Sandbox Code Playgroud)

那么,是(1)还是(2)?如果是前一种情况,那我的MWE有什么问题?

Col*_*mbo 5

对象或类型引用的列表初始化T定义如下:
(3.1) - 如果T是类类型,并且初始化列表具有单个元素类型cv U [..]
(3.2) - 否则,如果T是字符数组[..]
(3.3) - 否则,如果T是聚合,则执行聚合初始化(8.5.1).
(3.4) - 否则,如果初始化列表没有元素并且T是具有默认构造函数的类类型,则该对象是值初始化的.

A并且B都有基类,因此不是聚合.所以第四个要点适用.因此,我们具有与使用时完全相同的效果():

初始化器为空的括号集的对象,即 (),应进行值初始化.

任何与这些初始化器产生不同结果的编译器都不能符合要求.

处理protected成员访问的§11.4 没有提到与初始化形式相关的任何内容.但是,关于构造函数中的mem-initializer中的base的初始化,§11.4目前是CWG问题#1883所述的缺陷.