2010-09-30 5 views
7

Dans ce code:Pourquoi aurais-je besoin de conversion?

template<class T> 
struct Side 
{ 
}; 

template<class T> 
struct LeftSide : public Side<T> 
{ 
}; 
template<class T> 
struct RightSide : public Side<T> 
{ 
}; 

Side<int>* f(int left, int right) 
{ 
    return left < right ? new LeftSide<int> : new RightSide<int>;//<---Here I'm returning either left or right side 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    return 0; 
} 

Je reçois une erreur:
_ERROR 1 erreur C2446: ':': pas de conversion de 'Rightside *' à 'Leftside *' _

Je Je pensais (à tort comme je le vois) que je peux attribuer un pointeur de dérivé à la base sans aucun problème. Alors, où est le problème?

Répondre

14

Le problème est pas avec la conversion de deux LeftSide ou RightSide à Side<T>. Comme vous le pensiez à l'origine, cette conversion serait bien.

Au contraire, le problème est avec cette expression:

left < right ? new LeftSide<int> : new RightSide<int> 

Brisons ce un peu. L'opérateur ternaire (correctement mentionné dans la norme comme « opérateur de comparaison ») ressemble à ceci:

bool_val ? lhs_expression : rhs_expression 

Gardez à l'esprit que cette construction tout lui-même est une expression. Ce qui signifie qu'il renvoie une valeur, qui doit avoir un type, obv. Le type de l'expression entière est déduit des types lhs_expression et rhs_expression. Dans ce cas, vous avez un LeftSide et un RightSide. Alors, voici votre problème.

LeftSide et RightSide ne sont pas directement liés les uns aux autres autre que d'avoir une classe de base commune, et il n'y a pas de conversion disponible entre eux. (Vous devriez en écrire un.) Il n'y a donc pas de type de données unique que le bool_val ? lhs_expression : rhs_expression peut avoir. Vous pourriez penser, "eh bien, compilateur stupide, pourquoi ne pas simplement comprendre la classe de base commune et l'utiliser?" C'est, en effet, un peu de douleur. En laissant de côté l'argument que c'est juste ou faux, cela ne fonctionne tout simplement pas de cette façon.

Vous avez deux options.

Un, utilisez une simple construction:

if(left < right) 
    return new LeftSide<int>; 
else 
    return new RightSide<int>; 

Deux, si vous voulez vraiment vraiment utiliser l'opérateur ternaire (ce qui est parfois le cas), vous devez cuillère nourrir le compilateur, il est des types de données:

Side<int>* f(int left, int right) 
{ 
    return left < right ? static_cast<Side<int>*>(new LeftSide<int>) : static_cast<Side<int>*>(new RightSide<int>);// now you're good 
} 
+1

(1) Vous ne pouvez pas écrire des conversions définies par l'utilisateur entre les types de pointeurs. (2) Vous avez seulement besoin de fixer le type d'un opérande pour le faire fonctionner. (3) Même 'static_cast' est surchargé car une conversion implicite existe, et' static_cast' pourrait empêcher le compilateur d'avertir des problèmes légitimes de sécurité de type. –

4

Je pense que le? : l'opérateur exige que les 2 choix soient du même type; pas qu'ils peuvent être convertis au même type

Pour votre information gcc échoue même

error: conditional expression between distinct pointer types ‘LeftSide<int>*’ and ‘RightSide<int>*’ lacks a cast 

coulée à la fois à côté (int) * œuvres (mais vous saviez probablement déjà)

+2

La conversion est correcte, mais un opérande doit être convertible au type de l'autre. Si n'importe quel type est autorisé, le problème devient intraitable dans le cas général. –

+1

Je pense que vous avez raison mais c'est tellement décevant! –

2

Vous voulez à la fois branches pour renvoyer un Side<int>*, mais le compilateur ne le sait pas, le type Side<int> n'apparaît nulle part dans cette expression.

Depuis que je n'aime pas utiliser un casting quand une conversion implicite existe, j'écrire cela comme:

if (left < right) return new LeftSide<int>; 
return new RightSide<int>; 

Mais si vous voulez utiliser un opérateur ternaire,

Side<int>* i_want_this_type; 
return (left < right) ? new LeftSide<int> : (i_want_this_type = new RightSide<int>); 

Maintenant, la branche de droite est de type Side<int>*, la main gauche est convertible en ce type, tout est ok (et le compilateur optimise la variable supplémentaire).

+0

raison pour laquelle la baisse? –

+0

Je ne l'aurais pas fait mais je suppose que c'est parce que vous avez introduit un temporaire - même s'il est temporaire, le compilateur peut optimiser en vapeur. –

0

Les deux devraient être du même type, ou l'un devrait être convertible à l'autre.

return left < right ? (Side<int>*)new LeftSide<int> : (Side<int>*)new RightSide<int>; 
+0

Un casting de style C est complètement exagéré ici. –

+0

Et brutalement moche et vieux-out-of-date-programmeur-hasbeen-hack-ish –

Questions connexes