9
#include <iostream>
using namespace std;

struct CL2
{
    CL2(){}
    CL2(const CL2&){}
};

CL2 cl2;

struct CL1
{
    CL1(){}
    operator CL2&(){cout<<"operator CL2&"; return cl2;}
    operator const CL2&(){cout<<"operator const CL2&"; return cl2;}
};

CL1 cl1;

int main()
{
    CL1 cl1;
    CL2 cl2 (cl1);
}

clang 和 gcc 都给出了模棱两可的转换运算符,但 Visual Studio 编译正常并打印“operator const CL2&”。根据标准,必须如何正确?
正如我所理解的,将 CL1 转换为 const CL2& 是在复制初始化上下文中(作为 cl2 对象直接初始化的一部分)。我看过 n4296 草案,[over.match.copy]:

假设“cv1 T”是被初始化对象的类型,T是类类型,候选函数的选择如下:
— T的转换构造函数(12.3.1)是候选函数。
— 当初始化表达式的类型是类类型“cv S”时,考虑 S 及其基类的非显式转换函数。初始化临时绑定到构造函数的第一个参数时,其中参数的类型为“对可能 cv 限定的 T”的引用,并且在直接初始化类型对象的上下文中使用单个参数调用构造函数“cv2 T”,还考虑了显式转换函数。那些没有隐藏在 S 中并产生其 cv 非限定版本与 T 相同类型或者是其派生类的类型是候选函数。返回“对 X 的引用”的转换函数根据引用的类型返回类型 X 的左值或 x 值,因此在选择候选函数的过程中被认为产生 X。

即两个转换运算符都被视为返回 CL2 和 const CL2(不仅仅是没有 const 的 CL2),还有待解决,哪种转换更好:CL2 -> const CL2& 或 const CL2 -> const CL2&。第二种情况似乎更合适。在这种情况下是否应该考虑更好的资格转换?或者两种情况都是身份转换?我在标准中找不到它

4

1 回答 1

5

由于两个转换运算符具有相同的签名,因此一个优先于另一个的唯一方法是应用 [over.match.best]/(1.4)...</p>

— 上下文是通过用户定义的转换(参见 8.5、13.3.1.5 和 13.3.1.6)进行的初始化,从返回类型F1到目标类型(即,正在初始化的实体的类型)的标准转换序列是比从返回类型F2到目标类型的标准转换序列更好的转换序列。

……或(1.5):

— 上下文是对函数类型的引用的直接引用绑定 (13.3.1.6) 的转换函数的初始化,[…]

显然,两者都不适用,因此含糊不清。一种可能的消除歧义的方法:

operator CL2&();
operator const CL2&() const; 

演示;这里,根据[over.ics.rank]/(3.2.6),前一个重载的隐式对象参数的初始标准转换顺序更好,这是由[over.match.best]/(1.3)决定的。

于 2015-12-06T18:03:38.340 回答