重载转换函数模板

14
请看下面内容:

考虑以下事项:

struct X {
    template <class T> operator T();  // #1
    template <class T> operator T&(); // #2
};

int        a = X{}; // error: ambiguous
int&       b = X{}; // calls #2
int const& c = X{}; // calls #2

b的情况很简单,#2是唯一可行的候选。什么规则表明#2#1更适合初始化int const&,但对于初始化int,这两个都是模糊的?


1
[over.match.conv][over.match.ref]似乎是一个不错的起点。 - jaggedSpire
@jaggedSpire 这些部分指示了候选函数是什么,而不是如何偏好(或不偏好)其中之一。 - Barry
2
这里模板并不是必要的,普通的 operator intoperator int& 也是有歧义的。 - TemplateRex
那些不显式转换的函数,当初始化左值引用时,会产生类型为“lvalue reference to cv2 T2”的结果,其中“cv1 T”与“cv2 T2”具有引用兼容性([dcl.init.ref]),这些函数被认为是候选函数。这似乎对于初始化左值类型存在一种限制,这将使int const&无法符合条件。 - jaggedSpire
1
@TemplateRex 我知道这是不明确的。问题是为什么 a 是不明确的,而 c 不是。 - Barry
显示剩余2条评论
1个回答

3

在确定如何初始化引用时,首先尝试直接绑定。[dcl.init.ref]/(5.1.2):

如果引用是左值引用且初始化表达式[…]具有类类型(即T2是类类型),其中T1T2不相关,并且可以转换为类型“cv3 T3”的左值,其中“cv1 T1”与“cv3 T3”兼容(通过枚举适用的转换函数(13.3.1.6)并通过重载决议(13.3)选择最佳函数来选择此转换),则将引用绑定到转换的左值结果[...]。

这个过程的候选人选择规则(如上所述13.3.1.6)排除了第一个转换函数:
引用S及其基类的转换函数被视为候选函数。那些非显式转换函数,且未在S中隐藏,产生类型“对cv2T2的左值引用”(初始化左值引用或函数的右值引用时),其中“cv1T”与“cv2T2”兼容(8.6.3),是候选函数。对于直接初始化,[...]。
显然,这种排除是特定于引用初始化语义的,因此第一种情况仍然存在歧义。

但是您省略了该部分的一部分,该部分考虑产生 cv2 T2 的转换函数。那个其他候选函数发生了什么? - Barry
@Barry这只适用于“初始化rvalue引用或lvalue函数引用”,还是我没有理解你的意思? - jaggedSpire
@jaggedSpire 我本来想说,巴里让我有点困惑。如果一个转换函数返回一个 prvalue,那么一个 lvalue 引用怎么能直接绑定到它上面呢?这有点违背该部分的意图。 - Columbo
是的,这就是我所缺少的。因此,operator T() 是一个候选项的原因不是因为 13.3.1.6 这样说,而是因为我们回退到了 8.6.3/5.2.2.1 和 13.3.1.5 找到了它。对吗? - Barry
@Barry 候选人处于什么情况下? - Columbo
@Columbo 在 int const& c = X{} 中。假设 operator T&() 不存在。也许“候选项”这个词不太准确,因为在通常的重载决议方式中,两者不会同时考虑。 - Barry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接