web-dev-qa-db-fra.com

le littéral «0» étant un candidat valide pour les chaînes int et const et les surcharges provoquent un appel ambigu

J'ai récemment corrigé un bug.

Dans le code suivant, l'une des fonctions surchargées était const et l'autre non. Le problème sera résolu en rendant les deux fonctions const.

Ma question est pourquoi le compilateur ne s'en est plaint que lorsque le paramètre était 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;
}
44
Cong Ma

0 Est spécial en C++. Un pointeur nul a la valeur de 0 Donc C++ permettra la conversion de 0 En un type de pointeur. Cela signifie que lorsque vous appelez

a.f(0);

Vous pourriez appeler void f(int i = 0) const avec un int avec la valeur de 0, Ou vous pourriez appeler void f(const std::string&) avec un char* Initialisé à null.

Normalement, la version int serait meilleure car c'est une correspondance exacte mais dans ce cas, la version int est const, donc elle nécessite une "conversion" a vers un const CppSyntaxA, où la version std::string ne nécessite pas une telle conversion mais nécessite une conversion en char* puis en std::string. Ceci est considéré comme un changement suffisant dans les deux cas pour être considéré comme une conversion égale et donc ambigu. Faire les deux fonctions const ou non const corrigera le problème et la surcharge int sera choisie car c'est mieux.

56
NathanOliver

Ma question est pourquoi le compilateur ne s'en est plaint que lorsque le paramètre était 0.

Parce que 0 n'est pas seulement un littéral entier, mais c'est aussi un littéral de pointeur nul. 1 n'est pas un littéral de pointeur nul, il n'y a donc aucune ambiguïté.

L'ambiguïté provient du constructeur de conversion implicite de std::string qui accepte un pointeur sur un caractère comme argument.

Maintenant, la conversion d'identité de int en int serait autrement préférée à la conversion de pointeur en chaîne, mais il existe un autre argument qui implique une conversion: l'argument d'objet implicite. Dans un cas, la conversion est de CppSyntaxA& à CppSyntaxA& alors que dans les autres cas c'est CppSyntaxA& à const CppSyntaxA&.

Ainsi, une surcharge est préférée en raison d'un argument, et l'autre surcharge est préférée en raison d'un autre argument et il n'y a donc pas de surcharge préférée sans ambiguïté.

Le problème sera résolu en rendant les deux fonctions const.

Si les deux surcharges sont qualifiées const, alors la séquence de conversion de l'argument objet implicite est identique, et donc l'une des surcharges est sans ambiguïté préférée.

11
eerorika