clang 和 gcc 都接受以下代码并选择A::operator B*
.
struct B
{
};
struct A : B
{
operator A*();
operator B*();
};
A a;
void* x = a;
我对标准的阅读——特别是下面以粗体突出显示的句子——表明这种转换应该是模棱两可的。
两者A::operator A*
和A::operator B*
都是重载解决方案的候选者,因为A*
和B*
都可以void*
通过标准转换转换为。因为隐含对象参数A&
是唯一的参数,所以只考虑从隐含对象参数转换为隐含对象参数的转换序列 - 忽略转换函数产生的类型。在这两种情况下,隐含的对象参数是初始化表达式的 type A
,而隐含的对象参数是A&
。如果两个转换序列相同,则无法区分两个候选者。
8.5 初始化器 [dcl.init]
初始化器的语义如下。目标类型是正在初始化的对象或引用的类型,源类型是初始化表达式的类型。
— 如果目标类型是 [ reference/array/class ...] [已删除不适用于此场景的详细信息]
— 否则,如果源类型是(可能是 cv 限定的)类类型,则考虑转换函数。 列举了适用的转换函数 (13.3.1.5),并通过重载决议 (13.3) 选择最佳转换函数。调用如此选择的用户定义转换将初始化表达式转换为正在初始化的对象。如果转换无法完成或不明确,则初始化格式错误。
13.3.1.5 通过转换函数初始化[over.match.conv]
在 8.5 中指定的条件下,作为非类类型对象初始化的一部分,可以调用转换函数将类类型的初始化表达式转换为正在初始化的对象的类型。重载分辨率用于选择要调用的转换函数。假设“cv1 T”是被初始化对象的类型,“cv S”是初始化表达式的类型,其中S是类类型,候选函数选择如下:
— 考虑 S 及其基类的转换函数。那些不隐藏在 S 中并产生类型 T 或可以通过标准转换序列 (13.3.3.1.1) 转换为类型 T 的非显式转换函数是候选函数。对于直接初始化,那些不隐藏在 S 中并产生类型 T 的显式转换函数或可以通过限定转换 (4.4) 转换为类型 T 的类型也是候选函数。对于这个选择候选函数的过程,返回 cv 限定类型的转换函数被认为会产生该类型的 cv 非限定版本。返回“对 cv2 X 的引用”的转换函数返回左值或 x 值,具体取决于引用的类型,类型为“cv2 X”,因此在选择候选函数的过程中被认为产生 X。
参数列表有一个参数,即初始化表达式。[ 注意:此参数将与转换函数的隐式对象参数进行比较。——尾注]
根据标准,这是否模棱两可?
编辑:请注意,这是一个类似的问题,但与通过初始标准转换序列区分用户定义的转换序列不同
不同之处在于,在我的示例中,两个转换函数具有相同的资格。