Les points importants sont les suivants:
D'abord, il y a deux pertinentes de operator <
surcharges.
operator <(const Foo&, const Foo&)
. L'utilisation de cette surcharge nécessite une conversion définie par l'utilisateur du littéral 0
en Foo
en utilisant Foo(int)
.
operator <(int, int)
. L'utilisation de cette surcharge nécessite de convertir Foo
en bool
avec le operator bool()
défini par l'utilisateur, suivi d'un de promotion à int
(c'est, en standardese, différent d'une conversion, comme cela a été souligné par Bo Persson).
La question ici est: D'où vient l'ambiguïté? Certes, le premier appel, qui ne nécessite qu'une conversion définie par l'utilisateur, est plus sensible que le second, ce qui nécessite une conversion définie par l'utilisateur suivie d'une promotion?
Mais ce n'est pas le cas. La norme attribue un rang à chaque candidat . Cependant, il n'y a pas de classement pour "conversion définie par l'utilisateur suivie d'une promotion". Cela a le même rang que l'utilisation d'une conversion définie par l'utilisateur. Tout simplement (mais officieusement) mettre, la séquence de classement ressemble un peu à ceci:
- correspondance exacte
- (seulement) la promotion nécessaire
- (uniquement) conversion implicite nécessaire (y compris les « dangereux » hérités de C tels que
float
à int
)
- conversion définie par l'utilisateur requis
(non-responsabilité:. comme mentionné précédemment, c'est informel Il obtient beaucoup plus complexe lorsque mul il y a des arguments secondaires, et je n'ai pas non plus mentionné de références ou de qualification de cv. Ceci est juste une vue d'ensemble approximative.)
Cela explique, espérons-le, pourquoi l'appel est ambigu. Maintenant, pour la partie pratique de la façon de résoudre ce problème. Presque jamais quelqu'un qui fournit operator bool()
veut qu'il soit implicitement utilisé dans les expressions impliquant l'arithmétique entière ou les comparaisons. En C++ 98, il y avait des solutions de contournement obscures, allant de std::basic_ios<CharT, Traits>::operator void *
aux versions "améliorées" plus sûres impliquant des pointeurs vers des membres ou des types privés incomplets. Heureusement, C++ 11 introduit une manière plus lisible et cohérente d'empêcher la promotion d'entier après des utilisations implicites de operator bool()
, qui est de marquer l'opérateur comme explicit
. Cela supprimera entièrement la surcharge operator <(int, int)
, plutôt que de simplement la "rétrograder".
Comme d'autres l'ont mentionné, vous pouvez également marquer le constructeur Foo(int)
comme explicite. Cela aura pour effet inverse de supprimer la surcharge operator <(const Foo&, const Foo&)
.
Une troisième solution consisterait à prévoir des surcharges supplémentaires, par exemple:
operator <(int, const Foo&)
operator <(const Foo&, int)
Ce dernier, dans cet exemple, sera alors préférable à des surcharges mentionnées ci-dessus comme une correspondance exacte, même si vous n'avez pas introduit explicit
. La même chose va par exemple pour
operator <(const Foo&, long long)
qui serait préférable à operator <(const Foo&, const Foo&)
en a < 0
parce que son utilisation ne nécessite qu'une promotion.
'0' est de type' int', pas de type 'Foo' – Rafalon
Avez-vous vraiment besoin de ces deux conversions implicites? Les conversions implicites uniques peuvent causer des maux de tête, mais les conversions implicites à deux voies sont une véritable migraine. – TartanLlama
Rendre le constructeur à argument unique 'explicite' –