我有以下几点:
#include <iostream>
class Foo;
struct Test
{
template <typename T>
operator T() const // <----- This const is what puzzles me
{
std::cout << "Template conversion" << std::endl;
return T{};
}
operator Foo*()
{
std::cout << "Pointer conversion" << std::endl;
return nullptr;
}
};
int main()
{
Test t;
if (t)
{
std::cout << "ahoy" << std::endl;
}
bool b = (bool)t;
Foo* f = (Foo*)t;
}
它构建良好,但是当我运行它时,我希望得到
$> ./a.out
Template conversion
Template conversion
Pointer conversion
我改为
$> ./a.out
Pointer conversion
Pointer conversion
Pointer conversion
如果我删除const,或使Test实例为const,那么一切都按预期工作。 更确切地说,当两个运算符具有相同的const限定时,过载选择似乎是有意义的。
13.3.3.1.2标准的要点让我觉得我应该得到一个身份转换,转换为bool,使用模板转换运算符实例化T
= bool
,尽管有显然是隐藏在某处的微妙之处。有人可以告诉我这里有什么规则发挥作用吗?
答案 0 :(得分:6)
相关规则在[over.match.best]中定义:
鉴于这些定义,可行函数
F1
被定义为比另一个可行函数更好的函数F2
如果所有参数 i ,则ICS i (F1
)的转换序列不比ICS i 更差(F2
),然后
(1.3) - 对于某些参数 j ,ICS j (F1
)是比ICS j ({{更好的转换序列1}}),或者,如果不是那样,
(1.4) - 上下文是通过用户定义的转换(见8.5,13.3.1.5和13.3.1.6)以及从F2
的返回类型到目标类型的标准转换序列(即,的类型 正在初始化的实体)是一个比返回类型F1
到目标类型的标准转换序列更好的转换序列。
让我们看看第一个F2
案例。我们有两个可行的候选人:
bool
两者都使用非Test::operator T<bool>() const;
Test::operator Foo*();
const
进行调用。对于第二次重载,不需要转换 - 转换序列只是完全匹配。但是,对于第一次重载,隐式Test
参数需要进行从this
到Test
的限定转换。因此,第二个重载是首选 - 我们没有进入讨论返回类型的第二步。
如果我们放弃了const Test
,那么可行的候选人将成为:
const
此处,两个候选者在转换序列相同的情况下同样可行,但Test::operator T<bool>();
Test::operator Foo*();
模板是首选,因为从返回类型bool
到bool
的转换序列(身份 - 最高等级) )是一个比从bool
到Foo*
更好的转换序列(布尔转换 - 最低)。
答案 1 :(得分:3)
比较转换序列时,在转换结果类型之前会考虑参数的转换。隐式对象参数(this
指针)被视为参数,限定转换(Foo -> Foo const
)比隐式对象参数的标识转换更糟糕。来自 [over.match.best] :
1 - [...]一个可行的功能F1被定义为比另一个可行功能更好的功能 F2如果对于所有参数i,ICSi(F1)不是比ICSi(F2)更差的转换序列,然后
- 对于某些参数j,ICSj(F1)是比ICSj(F2)更好的转换序列,或者,如果不是,[...]
因此,非const
合格的成员转换运算符总是优于const
- 合格的成员转换运算符,即使结果转换与后者完全相同。