从多继承子类C构造B对象时的构造歧义问题探究
继承层级中构造函数的歧义问题解析
问题场景
当结构体C同时公有继承A和B,且B提供了接受const A&的构造函数时,使用C对象构造B会出现编译器行为不一致的情况:
struct A {}; struct B { B() {} B(const A&) {} }; struct C : A, B {}; B b1(C{}); // 所有编译器均通过 C c; B b2(c); // MSVC通过,GCC和Clang报歧义错误
GCC和Clang给出的错误信息如下:
error: call of overloaded 'B(C&)' is ambiguous note: candidate: 'B::B(const A&)' note: candidate: 'constexpr B::B(const B&)'
两种构造情况的差异分析
1. 临时C对象构造B(B b1(C{});)
GCC和Clang接受该写法是符合C++标准的。原因如下:
- 临时
C对象是右值(prvalue),匹配B的构造函数时有两个候选:B(const B&):将C右值转换为const B&,属于派生类到基类的标准转换,是精确匹配;B(const A&):将C右值转换为const A&,同样是派生类到基类的标准转换,也是精确匹配。
- 根据C++重载决议规则,当两个转换序列优先级相同时,若其中一个是隐式生成的拷贝构造函数,且实参是该类的派生类对象,则拷贝构造函数会被优先选择。这就是所有编译器接受该写法的核心原因。
2. 左值C对象构造B(B b2(c);)
GCC和Clang报错是正确的,MSVC的行为不符合标准:
- 左值
c是C&类型,匹配两个候选构造函数时:- 转换为
const B&需要为左值添加const限定,属于带const添加的精确匹配; - 转换为
const A&同样需要添加const限定,也是精确匹配。
- 转换为
- 此时两个转换序列的优先级完全相同,没有规则能区分哪个构造函数更优,因此重载决议出现歧义,编译器必须报错。
显式声明默认拷贝构造函数的影响
若显式声明B的拷贝构造函数:
B(const B&) = default;
此时GCC和Clang会对B b1(C{});也报歧义错误,这个行为是正确的:
- 显式声明拷贝构造函数后,它不再被视为“隐式生成的拷贝构造函数”,而是普通的用户声明构造函数。此时重载决议中不再有“拷贝构造函数优先匹配派生类实参”的特殊规则,两个候选构造函数的转换序列优先级完全相同,因此出现歧义。
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

