【发布时间】:2013-08-08 00:55:47
【问题描述】:
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& 是唯一的参数,所以只考虑从隐含对象参数转换为隐含对象参数的转换序列 - 忽略转换函数产生的类型。在这两种情况下,隐含的对象参数是初始化表达式的类型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 a 类类型,候选函数选择如下:
——考虑 S 及其基类的转换函数。 那些非显式转换 未隐藏在 S 中的函数并产生类型 T 或可以转换为类型 T 的类型 通过标准转换序列 (13.3.3.1.1) 是候选函数。 对于直接初始化,那些 未隐藏在 S 中的显式转换函数并产生类型 T 或可以 通过限定转换 (4.4) 转换为类型 T 也是候选函数。转换 返回 cv 限定类型的函数被认为产生该类型的 cv 非限定版本 对于这个选择候选函数的过程。返回“对 cv2 的引用”的转换函数 X”返回左值或xvalues,取决于引用的类型,类型为“cv2 X”,因此是 考虑为这个选择候选函数的过程产生 X。
参数列表有一个参数,即初始化表达式。 [注意:这个参数将是 与转换函数的隐式对象参数进行比较。 ——尾注]
按照标准这是模棱两可的吗?
编辑:注意这是一个类似的问题,但与Distinguishing between user-defined conversion sequences by the initial standard conversion sequence不同
不同之处在于,在我的示例中,两个转换函数具有相同的限定条件。
【问题讨论】:
-
两个转换函数都返回一个指向实例的相同基地址的指针,这是转换为
void*导致的。尝试多重继承和额外的转换运算符,转换应该是模棱两可的。 -
@CaptainObvlious 你怎么知道他们返回了什么? They do not even need to be implemented to test this question.
-
@Casey 因为转换运算符有一个返回类型来指定它们返回的内容。
-
@CaptainObvlious 知道返回类型不允许人们推断它们“返回指向实例相同基地址的指针”。
标签: c++ initialization standards overloading language-lawyer