模板转换运算符优先级&常量性

时间:2015-05-06 12:59:39

标签: c++ templates overload-resolution

我有以下几点:

#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,尽管有显然是隐藏在某处的微妙之处。有人可以告诉我这里有什么规则发挥作用吗?

2 个答案:

答案 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参数需要进行从thisTest的限定转换。因此,第二个重载是首选 - 我们没有进入讨论返回类型的第二步。

如果我们放弃了const Test,那么可行的候选人将成为:

const

此处,两个候选者在转换序列相同的情况下同样可行,但Test::operator T<bool>(); Test::operator Foo*(); 模板是首选,因为从返回类型boolbool的转换序列(身份 - 最高等级) )是一个比从boolFoo*更好的转换序列(布尔转换 - 最低)。

答案 1 :(得分:3)

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

  

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

     

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

因此,非const合格的成员转换运算符总是优于const - 合格的成员转换运算符,即使结果转换与后者完全相同。