考虑以下简单示例
struct C
{
template <typename T> operator T () {return 0.5;}
operator int () {return 1;}
operator bool () {return false;}
};
int main ()
{
C c;
double x = c;
std::cout << x << std::endl;
}
使用Clang编译时,会出现以下错误
test.cpp:11:12: error: conversion from 'C' to 'double' is ambiguous
double x = c;
^ ~
test.cpp:4:5: note: candidate function
operator int () {return 1;}
^
test.cpp:5:5: note: candidate function
operator bool () {return false;}
^
test.cpp:3:27: note: candidate function [with T = double]
template <typename T> operator T () {return 0.5;}
^
1 error generated.
其他编译器会产生类似的错误,例如GCC和Intel iclc
如果我删除operator int
和operator bool
。它编译好并按预期工作。如果仅删除其中一个,即保留模板运算符并说出operator int
,则始终选择非模板版本。
我的理解是,只有当模板和非模板重载函数在完全匹配或两者都需要相同转换序列的意义上相等时,非模板版本才是首选。但是在这种情况下,似乎编译器没有将操作员模板视为完美匹配。如果同时存在bool
和int
重载,那么它自然会认为它们不明确。
总之,我的问题是为什么在这种情况下操作员模板不被认为是完美的匹配?
答案 0 :(得分:8)
让我们将其分解为两个不同的问题:
<强> 1。为什么会产生编译错误?
struct C
{
operator bool () {return false;}
operator int () {return 1;}
};
由于int
和bool
都可以隐式转换为double
,因此编译器无法知道它应该使用哪个函数。它可以使用两个函数,并且没有一个优先于另一个函数。
<强> 2。为什么模板版本不是完美匹配?
struct C
{
template <typename T> operator T () {return 0.5;}
operator int () {return 1;}
};
为什么在请求双倍时调用operator int()
?
调用非模板函数是因为非模板函数优先于重载决策。 (Overloading function templates)
修改强> 我错了!正如严周在他的评论中提到的那样,正如我提供的链接中所述,模板化函数中的完美匹配优先于非模板化函数。
我测试了你的代码(用g ++ 4.7.2编译),它按预期工作:它返回0.5
,换句话说,使用了模板化函数!
<强> EDIT2:强> 我现在尝试使用clang,我可以重现你描述的行为。由于它在gcc中正常工作,这似乎是clang中的错误。
答案 1 :(得分:5)
这很有趣。有两种方法可以阅读第13.3.3节的关键部分。原始示例肯定应该调用函数模板,但删除其中一个非模板的版本可能会被认为是不明确的。
13.3.3:
如果对于所有参数 i ,ICS_i({{1>},可行函数
F1
被定义为更好函数而不是另一个可行函数F2
。 {1}})转换序列不比ICS_i(F1
)差,然后
对于某些参数 j ,ICS_j(
F2
)是比ICS_j(F1
)更好的转换序列,或者,如果不是,上下文是通过用户定义的转换(参见8.5,13.3.1.5和13.3.1.6)以及从
F2
的返回类型到目标类型的标准转换序列(即,正在初始化的实体的类型是比从F1
的返回类型到目标类型的标准转换序列更好的转换序列,或者,如果不是,
F2
是非模板函数,F1
是函数模板特化,或者,如果不是,
F2
和F1
是函数模板特化,根据部分排序规则,F2
的函数模板比F1
的模板更专业化在14.5.6.2中描述。如果只有一个可行的函数比所有其他可行函数更好的函数,则它是由重载决策选择的函数;否则电话会形成不良。
在该示例中,clang正确识别了三个可行候选函数的集合:
F2
第三个是功能模板专业化。 (我不认为上面的语法是合法的,但你明白了:在重载解析的这一点上,它不被视为模板,而是作为具有明确函数类型的特化。)
此处参数的唯一隐式转换序列(ICS1)与隐式参数上的“左值C::operator int()
C::operator bool()
C::operator double<double>()
”到“C
”完全匹配,因此不会产生任何影响。
这个例子正是第二个子弹中描述的情况,因此返回C&
的函数明显优于其他两个。
这里变得奇怪:通过非常字面的阅读,double
也比模板专业化更好,因为第三个子弹。 “等一下,不应该'优于'反对称?你怎么能说operator int
比F1
好,F2
优于F2
?”不幸的是,标准没有明确说明任何类型。 “第二颗子弹是不是优先于第三颗子弹,因为'如果不是那个'这个短语?”是的,对于常数F1
和F1
。但标准并没有说满足F2
的第二个子弹使(F1,F2)
的第三个子弹不适用。
当然,由于(F2,F1)
并不比operator int
好,反之亦然,因此仍然“只有一个可行的功能比所有其他可行的功能更好”。
我并不完全支持这种奇怪的阅读,除非将其报告为标准缺陷。与之相关会产生奇怪的后果(比如删除一个重载,这个不是从这个例子中最好的将程序从格式良好变为模糊!)。我认为目的是在考虑第三个子弹之前,考虑两种方式。
这意味着应该通过重载决策选择函数模板,这是一个铿锵的错误。