gcc *有时*以奇怪的方式解决过载歧义

时间:2014-05-02 08:34:55

标签: c++ gcc

回答之前:这不是关于如何让这段代码做我想做的事情的问题。我已经知道如何做到这一点(见本问题的结尾)。这是一个关于理解编译器为什么会这样做的问题。

请考虑以下(简化)代码:

#include <iostream>

void operator>>( std::istream &stream, char chr )
{
    std::cout<<"Called "<<chr<<"\n";
}

int main()
{
    char c='b';
    std::cin>>c;

    return 0;
}

使用gcc 4.8.2编译-Wall -Wextra会产生一个不相关的警告(流未使用)。但是,在运行时,打印&#34;称为b&#34;。

我预计会发生两件事之一。程序将从标准输入中读取一个字符,否则代码将无法编译,因为编译器发现我的运算符和标准库定义的运算符不明确。根据我的理解,标准库定义的运算符等同于:

std::istream &operator>>( std::istream &stream, char &c )

无论哪种方式,我都没想到要调用我的算子。

甚至更奇怪的是,由于上面提到的含糊不清,以下代码无法编译:

#include <iostream>

void function(char &chr)
{
    std::cout<<"1 "<<chr<<"\n";
}

void function(char chr)
{
    std::cout<<"2 "<<chr<<"\n";
}

int main()
{
    char c='a';
    function(c);

    return 0;
}

除了使用函数而不是运算符之外,我没有看到编译器对这两种情况的决定有任何区别。

注意:我完全意识到标准库定义的内容与我上面编写的原型不完全相同。我认为,在解决功能时,至少在这个用例中,重要的是没有区别。请记住,std定义不能有默认参数,因为这是一个运算符重载。

另外,正如我在问题的开头写的那样,我已经知道如何使代码做正确的事情。使用&#34; const char&amp;&#34;定义我的运算符而不是&#34; char&#34;导致编译器选择正确的重载。

2 个答案:

答案 0 :(得分:5)

对于电话std::cin>>c;,有两个可行的功能。你宣布的那个

void operator>>( std::istream &stream, char chr );

和流库中定义的那个(作为函数模板的特化):

template< class CharT, class Traits >
basic_istream<CharT,Traits>& operator>>( basic_istream<CharT,Traits>& st, CharT& ch );

选择最终赢得重载决策的最佳可行功能,如13.3.3 [over.match.best]

中所述
  

1定义ICSi(F)如下:

     
    

- 如果F是静态成员函数,则为ICS1     (F)定义为ICS1(F)既不比ICS1更好也不差     (G)对于任何函数G,并且对称地,ICS1(G)既不好     也不比ICS1(F)差;否则,

         

- 让ICSi(F)表示     隐式转换序列,转换中的第i个参数     列出可行函数F的第i个参数的类型.13.3.3.1     定义隐式转换序列和     13.3.3.2定义一个隐式转换序列比一个更好的转换序列或更差的转换序列意味着什么     另一个。

  
     

鉴于这些定义,一个可行的功能F1被定义为更好   函数比另一个可行函数F2,如果对于所有参数i,   ICSi(F1)转换序列不比ICSi(F2)差,然后

     
    

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

         

- 上下文是初始化的     用户定义的转换(见8.5,13.3.1.5和13.3.1.6)和     标准转换序列从F1的返回类型到     目的地类型(即,被初始化的实体的类型)是a     比标准转换序列更好的转换序列     F2的返回类型到目的地类型     [示例:省略]或者,如果不是,

         

- F1是非模板功能     和F2是功能模板专业化,或者,如果不是,

         

- F1和F2是功能模板专业化和功能     F1的模板比F2的模板更专业     到14.5.6.2中描述的部分排序规则。

  
     

2如果只有一个可行的功能是更好的功能   比所有其他可行的功能,然后它是由选择的   超载分辨率;否则电话会形成不良。

为了简化它 - 在其他条件相同的情况下,非模板比模板特化更好。

function的示例中,候选人之间没有这样的区别。调用所需的两个隐式转换序列(char - &gt; char&char - &gt; char)都是身份转换,并且不明确。

答案 1 :(得分:3)

标准operator >>是一个模板。非模板胜过模板(13.3.3 / 1),前提是参数转换质量相同(13.3.3.2/3)(还有其他条件,但这里不适用)。

由于char lvalue -> charchar lvalue -> char&都不比另一个好,因此非模板获胜。

如果您使用const char&声明非模板,则它会比标准>>更糟糕,因为const char&的限定符多于char&。所以std::operator>>获胜。

还有另一个原因:Koenig查找。标准operator>>是命名空间std的成员,只有在“正常”查找失败时才会找到它。如果您使用using namespace std,Koenig查找不会是一个因素。前面的内容不正确。