从子类构造类时存在歧义

问题描述 投票:0回答:1

这个问题的灵感来自于我的示例中的继承层次结构有什么问题?

考虑我们有一个结构体

B
,它可以通过对另一个结构体
A
的const引用来构造。
B
可以从同时继承
A
B
的对象构造吗?

struct A {};

struct B {
    B() {}
    B(const A&) {}
};

struct C : A, B {};

B b1(C{}); // 1. ok everywhere
C c;
B b2(c);   // 2. ok in MSVC, error in GCC and Clang

所有编译器都接受从临时构造

B b1(C{});
的第一个选项。从左值
B b2(c);
构造的第二个选项仅被 MSVC 接受,而 GCC 和 Clang 都因歧义错误而拒绝它:

error: call of overloaded 'B(C&)' is ambiguous
note: candidate: 'B::B(const A&)'
note: candidate: 'constexpr B::B(const B&)'

在线演示:https://godbolt.org/z/hTM6c5M73

GCC 和 Clang 接受案例 1 真的正确吗?如果在

B
:

中另外声明默认复制构造函数,他们改变主意是否正确?
B(const B&) = default;

在线演示:https://godbolt.org/z/9j9xeqY3r

c++ language-lawyer copy-constructor ambiguous
1个回答
0
投票

B b2(c);
是不明确的,因为
B
有两个可行的构造函数:

B(const A&);
B(const B&); // implicitly-declared copy constructor
// implicitly-declared move constructor not viable

两者都将

c
左值直接绑定到其参数,但具有每个 [over.ics.ref]/1 的派生到基数转换序列(而不是身份序列)的等级。转换序列在最佳重载方面是无法区分的,因为
A
都不是从
B
派生的,也不是
B´ derived from 
A` 派生的。 [over.ics.rank]/3[over.ics.rank]/4 中的规则均无法区分它们。


B b1(C{});
是明确的,因为还有另一个可行的重载

B(B&&); // implicitly-declared move constructor

这将参数直接绑定到右值引用,使其被认为比每个 [over.ics.rank]/3.2.3.

的其他两个重载更好

添加

B(const B&) = default;
通过删除
B(B&&);
移动构造函数来修改重载集,因为如果每个 [class.copy.ctor]/8.1.

有用户声明的复制构造函数,则不会隐式声明它。

然后

B b1(C{});
与之前一样在其他两个剩余重载之间是不明确的。

© www.soinside.com 2019 - 2024. All rights reserved.