c++ - conversions implicites avec l'opérateur ternaire

j'ai le code suivant:

class A {
public:
    operator int() const { return 5; }
};

class B {
public:
    operator int() const { return 6; }
};

int main() {
    A a;
    B b;
    int myInt = true ? a : b;
    return 0;
}

tentative de compiler ce code avec Visual Studio 2017 RC se traduit par l'erreur:

ternarytest.cpp(14): error C2446: ':': no conversion from 'B' to 'A'

ce qui est surprenant car on s'attendrait à ce qu'il les convertisse tous les deux en un type commun (int).

Clang 4.0 compile le même code avec succès sans aucune erreur ni avertissement.

Lequel des deux est correct dans ce cas?

16
demandé sur user1000039 2017-01-13 17:45:07

1 réponses

clang est à droite (voir les références ci - dessous), Car il n'y a pas de conversions possibles entre A et B, la résolution de surcharge est utilisée pour déterminer les conversions à appliquer aux opérandes, et la surcharge suivante (fictive) est sélectionnée:

int operator?:(bool, int, int);

Il existe un (encore une fois, fictive) de surcharge de l' ?: opérateur pour toute paire d'arithmétique (voir les références ci-dessous).


les règles:

depuis que vous ne peut pas convertir AB ou BA, alors ce qui suit s'applique ( [expr.cond]):

sinon, le résultat est une prvalue. Si les deuxième et troisième opérandes n'ont pas le même type, et soit type de classe, la résolution de surcharge est utilisée pour déterminer les conversions (le cas échéant) à être appliqué aux opérandes (13.3.1.2, 13.6). Si la résolution de surcharge échoue, le programme est mal formé. Autrement, le les conversions ainsi déterminées sont appliquées, et les opérandes converties sont utilisées à la place de l'original opérandes pour le reste de cette section.

Cela revient à ce ([sur.correspondre.oper]):

si l'un ou l'autre opérande a un type qui est une classe ou une énumération, une fonction d'opérateur définie par l'utilisateur pourrait être déclaré que met en œuvre cet opérateur ou un conversion définie par l'utilisateur peut être nécessaire de convertir l'opérande pour un type qui est approprié pour un opérateur.

[...]

L'ensemble des fonctions candidates pour la résolution de surcharge est l'union des états candidats, le non-membre candidats, et le haut-candidats.

si un candidat intégré est sélectionné par résolution de surcharge, les opérandes de type class sont converties en les types des paramètres correspondants de l'opération sélectionnée la fonction, sauf que la deuxième norme la séquence de conversion d'une séquence de conversion définie par l'utilisateur (13.3.3.1.2) n'est pas appliquée.

dans votre cas, il y a un candidat intégré (merci à @cpplearner, [sur.construit]):

pour chaque paire de types arithmétiques promus L et R, Il existe des fonctions d'opérateur candidat de la forme

LR      operator?:(bool, L, R);

où LR est le résultat des conversions arithmétiques habituelles entre les types L et R. [ Note: Comme pour toutes ces descriptions de fonctions candidates, la présente Déclaration ne sert qu'à décrire l'opérateur intégré aux fins de la résolution de la surcharge. L'opérateur "?:" ne peut pas être surchargé.  - fin de la remarque ]


détails supplémentaires:

Depuis le ?: l'opérateur ne peut pas être surchargé, cela signifie que votre code ne fonctionne que si les deux types peuvent être convertis en un type arithmétique (int). Comme un "compteur" - exemple, le code suivant est mal formé:

struct C { };
struct A { operator C() const; };
struct B { operator C() const; };

auto c = true ? A{} : B{}; // Error

notez aussi que vous obtiendriez un "appel" ambigu si l'un des types est convertible en deux types arithmétiques différents, par exemple,int et float. L'erreur de gcc) est en fait plein d'informations:

erreur: Aucune correspondance avec l'opérateur ternary?:' (opérande de ce type sont 'bool', 'A', 'B')

auto c = true ? A{} : B{};
~~~~~^~~~~~~~~~~
  • remarque: les candidat (e): opérateur?:(boolean, float, int)
  • note: candidat: opérateur?:(boolean, float, float)
13
répondu Holt 2018-04-12 19:26:35