模板转换运算符优先级和恒常性

Template conversion operator priority & constness

本文关键字:常性 优先级 运算符 转换      更新时间:2023-10-16

我有一些类似的东西:

#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 资格时,重载选择似乎严格有意义。

标准的 13.3.3.1.2 点让我认为我应该使用模板转换运算符实例化进行恒等转换,转换为布尔值,T=bool,尽管显然隐藏在某处的微妙之处。有人可以启发我这里有什么规则吗?

相关规则在 [over.match.best] 中定义:

根据这些定义,一个可行的函数F1被定义为比另一个可行的函数更好的函数F2如果对于所有参数i,ICSi(F1) 不是一个比 ICSi(F2) 更差的转换序列,然后 (1.3) — 对于某些参数 j,ICS j(
F1) 是比 ICSj(F2更好的转换序列,或者,如果不是这样,
(1.4) — 上下文是通过用户定义的转换进行初始化(参见 8.5, 13.3.1.5和13.3.1.6)和从返回类型F1到目标类型的标准转换序列(即,返回类型为目标类型(即,返回类型为目标类型)。 正在初始化的实体)是比从返回类型F2到目标类型的标准转换序列更好的转换序列。

让我们看看第一个bool案例。我们有两个可行的候选人:

Test::operator T<bool>() const;
Test::operator Foo*();

两者都使用非constTest调用。对于第二次重载,不需要转换 - 转换序列只是完全匹配。但是,对于第一次重载,隐式this参数需要进行从Testconst Test的限定转换。因此,首选第二个重载 - 我们不进入讨论返回类型的第二步。

但是,如果我们放弃const,可行的候选人将变为:

Test::operator T<bool>();
Test::operator Foo*();

在这里,两个候选对象在相同的转换序列下同样可行,但首选bool模板,因为从返回类型boolbool(标识 - 最高等级)的转换序列比从Foo*bool(布尔转换 - 最低)的转换序列更好。

比较转换序列时,在转换结果类型之前会考虑参数的转换。 隐式对象参数(this指针)被视为参数,限定转换(Foo -> Foo const)比隐式对象参数上的标识转换差。 来自[over.match.best]

1 - [...] 一个可行的函数F1被定义为比另一个可行的函数更好的函数 F2 如果对于所有参数 i,ICSi(F1) 不是比 ICSi(F2) 更差的转换序列,然后

— 对于某些参数 j,ICSj(F1) 是比 ICSj(F2) 更好的转换序列,或者,如果不是这样,[...]

因此,非const限定成员转换运算符始终优于const限定成员转换运算符,即使结果转换在后者上是精确的。