Question

class Foo {
  public:
  explicit Foo(double item) : x(item) {}

  operator double() {return x*2.0;}

  private:
  double x;
}

double TernaryTest(Foo& item) {
  return some_condition ? item : 0;
}

Foo abc(3.05);
double test = TernaryTest(abc);

Dans l'exemple ci-dessus, pourquoi le test est-il égal à 6 (au lieu de 6.1) si la condition est vraie?

Changer le code comme ci-dessous renvoie la valeur 6.1

double TernaryTest(Foo& item) {
  return some_condition ? item : 0.0; // note the change from 0 to 0.0
}

Il semble que (dans l'exemple original) la valeur de retour de Foo :: operator double soit convertie en un int puis en un double. Pourquoi?

Était-ce utile?

La solution

L'opérateur conditionnel vérifie les conversions dans les deux sens. Dans ce cas, puisque votre constructeur est explicite (le ?: n'est donc pas ambigu), la conversion de Foo en int est utilisée, à l'aide de votre fonction de conversion qui convertit en double : cela fonctionne car après l'application de la fonction de conversion, une conversion standard qui convertit le double en int (troncature ) suit. Le résultat de ?: dans votre cas est int et a la valeur 6 .

Dans le second cas, l'opérande ayant le type double , cette conversion de fin en int n'a pas lieu et le type de résultat ?: a le type double avec la valeur attendue.

Pour comprendre le "inutile" " Dans les conversions, vous devez comprendre que des expressions telles que votre ?: sont évaluées "sans contexte": lors de la détermination de la valeur et du type de celle-ci, le compilateur ne considère pas qu'il s'agit de l'opérande d'un < code> return pour une fonction renvoyant un double .

Modifier: que se passe-t-il si votre constructeur est implicite ? L'expression ?: sera ambiguë, car vous pouvez convertir un int en une valeur rvalue de type Foo (à l'aide du constructeur) et un < code> Foo à une valeur rvalue de type int (à l'aide de la fonction de conversion). La norme dit

  

En utilisant ce processus, il est déterminé si le deuxième opérande peut être converti pour correspondre au troisième opérande et si le troisième opérande peut être converti pour correspondre au deuxième opérande. Si les deux peuvent être convertis, ou un converti mais que la conversion est ambiguë, le programme est mal formé.

Paragraphes expliquant comment votre Foo est converti en int :

5.16 / 3 à propos de condition? E1: E2 :

  

Sinon, si les deuxième et troisième opérandes ont des types différents et qu’ils ont soit un type de classe (éventuellement qualifié de cv), on tente de convertir chacun de ces opérandes en types de l’autre. [...] E1 peut être converti pour correspondre à E2 si E1 peut être converti implicitement au type qu'aurait l'expression E2 si E2 était converti en une valeur rvalue (ou le type dont il dispose, si E2 est une valeur rvalue).

4.3 à propos de "converti implicitement":

  

Une expression e peut être implicitement convertie en un type T si et seulement si la déclaration T t = e; est bien formée, pour une variable temporaire inventée t.

8.5 / 14 sur l'initialisation de la copie ( T t = e; )

  

Si le type de source est un type de classe (éventuellement qualifié cv), les fonctions de conversion sont considérées. Les fonctions de conversion applicables sont énumérées (13.3.1.5), et la meilleure est choisie par résolution de surcharge (13.3). La conversion définie par l'utilisateur ainsi sélectionnée est appelée pour convertir l'expression d'initialisation en objet en cours d'initialisation. Si la conversion est impossible ou ambiguë, l’initialisation est mal formée.

13.3.1.5 sur les candidats à la fonction de conversion

  

Les fonctions de conversion de S et ses classes de base sont considérées. Celles qui ne sont pas cachées dans S et qui fournissent un type T ou un type pouvant être converti en type T via une séquence de conversion standard (13.3.3.1.1) sont des fonctions candidates.

Autres conseils

Ceci est couvert dans les détails qui prêtent à confusion dans la section 5.16 de la norme. La partie importante se trouve au paragraphe 3. "Si E2 est une lvalue: E1 peut être converti pour correspondre à E2 si E1 peut être converti implicitement (clause 4) en type" référence à T2 ", sous réserve de la contrainte définie dans la conversion. la référence doit lier directement (8.5.3) à E1. "

Dans l'expression, la seule valeur lvalue est élément . La question est donc de savoir si 0 (un entier) peut être implicitement converti en type Foo . Dans ce cas, il n'y a pas de conversion implicite d'aucun autre type en Foo , car la seule fonction de conversion disponible est marquée explicit . Par conséquent, cela ne fonctionne pas et nous suivons avec "si E2 est une valeur ou si la conversion ci-dessus ne peut pas être effectuée:" (en ignorant la partie indiquant si les deux ont un type de classe) "Sinon (c’est-à-dire si E1 ou E2 a un type non-classe, ou si les deux ont des types de classe mais que les classes sous-jacentes ne sont ni les mêmes, ni une classe de base du other): E1 peut être converti pour correspondre à E1 si E1 peut être converti implicitement au type que l'expression E2 aurait si E2 était converti en une valeur rvalue (ou le type dont il dispose, si E2 est une valeur rvalue). "

Par conséquent, nous voyons que 0 est une rvalue, de type int . Nous pouvons convertir un Foo , puisque nous pouvons implicitement convertir un Foo en un double et ensuite en un int . Puis:

"En utilisant ce processus, il est déterminé si le deuxième opérande peut être converti pour correspondre au troisième opérande et si le troisième opérande peut être converti pour correspondre au deuxième opérande. Si les deux peuvent être convertis, un ou plusieurs peuvent être convertis mais que la conversion est ambiguë, le programme est mal formé. Si aucun des deux ne peut être converti, les opérandes restent inchangés et une vérification supplémentaire est effectuée comme décrit ci-dessous. Si exactement une conversion est possible, cette conversion est appliquée à l'opérande choisi et l'opérande converti est utilisé à la place de l'opérande d'origine pour le reste de cette section. "

Puisque nous pouvons convertir un Foo en un int , nous convertissons le Foo en un int pour le reste de la détermination. Nous avons maintenant deux int comme types d'expression, et au moins un est une valeur rvalue.

Je peux continuer avec les paragraphes 5 et 6, mais je pense qu'il est assez évident que l'expression soit de type int .

Je pense que les plats à emporter sont:

  1. Votre compilateur fonctionne conformément à la norme.

  2. Les règles sur le type d'une expression conditionnelle sont trop compliquées pour être apprises facilement. Ne poussez pas l'enveloppe, car vous ferez parfois une erreur. (En outre, c’est exactement le type d’endroit où un compilateur pourrait échouer à appliquer la norme avec précision.)

  3. Essayez de spécifier les types afin que les deuxième et troisième expressions soient du même type. Dans tous les cas, essayez d’éviter les expressions qui ne sont pas du type souhaité.

Le type de l'expression ternaire est déterminé au moment de la compilation. peu importe ce que certaines conditions sont à l'exécution.

Je suppose que la question est alors: pourquoi le compilateur choisit-il int au lieu de double dans le premier exemple?

L'opérateur ternaire devine le type à partir de ses arguments. il ne peut pas convertir un objet en entier, mais il peut convertir un objet en un double qu’il convertit ensuite en entier.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top