C++ 文字“0”是int和const字符串的有效候选字符串&;重载导致调用不明确

C++ 文字“0”是int和const字符串的有效候选字符串&;重载导致调用不明确,c++,overloading,ambiguous-call,C++,Overloading,Ambiguous Call,我最近修复了一个bug 在下面的代码中,一个重载函数是const,另一个不是。该问题将通过使两个函数均为常量来解决 我的问题是为什么编译器只在参数为0时才抱怨它 #include <iostream> #include <string> class CppSyntaxA { public: void f(int i = 0) const { i++; } void f(const std::string&){} }; int main() {

我最近修复了一个bug

在下面的代码中,一个重载函数是const,另一个不是。该问题将通过使两个函数均为常量来解决

我的问题是为什么编译器只在参数为0时才抱怨它

#include <iostream>
#include <string>

class CppSyntaxA
{
public:
    void f(int i = 0) const { i++; }
    void f(const std::string&){}
};

int main()
{
    CppSyntaxA a;
    a.f(1);  // OK
    //a.f(0);  //error C2666: 'CppSyntaxA::f': 2 overloads have similar conversions
    return 0;
}
#包括
#包括
综合类群
{
公众:
void f(int i=0)const{i++;}
void f(const std::string&){
};
int main()
{
综合分类群;
a、 f(1);//好的
//a、 f(0);//错误C2666:'CppSyntaxA::f':2个重载具有类似的转换
返回0;
}

<代码> < p> <代码> 0 <代码>在C++中是特殊的。空指针的值为“代码>0”/CODE>,因此C++将允许<代码> 0 >代码>转换为指针类型。也就是说当你打电话的时候

a.f(0);
您可以使用值为
0
int
调用
void f(int i=0)const
,也可以使用初始化为null的
char*
调用
void f(const std::string&)

通常情况下,
int
版本会更好,因为它是精确匹配的,但在这种情况下,
int
版本是
const
,因此需要将
a
转换为
const-CppSyntaxA
,其中,
std::string
版本不需要这种转换,但需要先转换为
char*
,然后再转换为
std::string
。在这两种情况下,这被认为是足够的变化,因此被认为是相等的转换,因此不明确。将两个函数设置为
const
或非
const
将解决此问题,并且将选择
int
重载,因为它更好

我的问题是为什么编译器只在参数为0时才抱怨它

#include <iostream>
#include <string>

class CppSyntaxA
{
public:
    void f(int i = 0) const { i++; }
    void f(const std::string&){}
};

int main()
{
    CppSyntaxA a;
    a.f(1);  // OK
    //a.f(0);  //error C2666: 'CppSyntaxA::f': 2 overloads have similar conversions
    return 0;
}
因为0不仅是一个整数文本,而且也是一个空指针文本。1不是空指针文本,因此不存在歧义

歧义源于
std::string
的隐式转换构造函数,该构造函数接受指向字符的指针作为参数

现在,从int到int的标识转换比从指针到字符串的转换更可取,但是还有另一个涉及转换的参数:隐式对象参数。在一种情况下,转换是从
CppSyntaxA&
CppSyntaxA&
,而在另一种情况下,转换是从
CppSyntaxA&
const CppSyntaxA&

因此,一个重载是因为一个参数而首选的,而另一个重载是因为另一个参数而首选的,因此没有明确的首选重载

该问题将通过使两个函数均为常量来解决


如果两个重载都是限定的
const
,那么隐式对象参数转换序列是相同的,因此其中一个重载无疑是首选的。

@NeilButterworth GCC显示了@Miles不,它不是。@NeilButterworth确实是这样做的:
clang
似乎很满意它的价值。显然,我无法说明缺少错误消息的原因,但GCC 8.1.0使用-std=c++11和-std=c编译了它++17@FrançoisAndrieux我认为f(int)是常量,
a
是非常量变量。所以有一个从非常量到常量的转换。@FrançoisAndrieux别担心,编译器实现者也会这样做;)@JesperJuhl是一个空指针,
nullptr
是不同的东西。A同时也是
nullptr\u t
的一个prvalue。这个重载问题最令人恼火的部分是,用空指针调用
std::string
的构造函数是不可能的:/null指针的值是
0
?那是新的。它的实现定义了它的值,尽管literal
0
可以隐式转换为任何空指针类型,因此它将比较相等<代码>(intptr_t)(void*)0==0
不受保证,尽管在(大多数?)现代平台上仍然适用@JesperJuhl似乎你在
nullptr
上偏离了轨道。