2010-09-30 24 views
7

In diesem Code:Warum sollte ich konvertieren?

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; 
} 

Ich erhalte eine Fehlermeldung:
_Error 1 Fehler C2446: ':': keine Konvertierung von 'Rightside *' auf 'Leftside *' _

I‘ Ich dachte (zu Unrecht, wie ich sehe), dass ich den Zeiger ohne Probleme von abgeleitet auf basis zuweisen kann. Wo ist das Problem?

Antwort

14

Das Problem ist nicht mit der Umwandlung von entweder LeftSide oder RightSide zu Side<T>. Wie Sie ursprünglich dachten, wäre diese Umwandlung in Ordnung.

Vielmehr ist das Problem mit diesem Ausdruck:

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

ist dies ein wenig Lassen Sie brechen. Der ternäre Operator (richtig in Standard als ‚Vergleichsoperator‘ genannt) sieht wie folgt aus:

bool_val ? lhs_expression : rhs_expression 

Beachten Sie, dass dieses ganze Konstrukt ist selbst ein Ausdruck. Das heißt, es gibt einen Wert zurück, der einen Typ haben muss, obv. Der Typ des ganzen Ausdrucks wird von den Typen lhs_expression und rhs_expression abgeleitet. In diesem Fall haben Sie eine LeftSide und eine RightSide. Also, hier ist dein Problem.

LeftSide und RightSide sind nicht direkt miteinander verwandt, außer eine gemeinsame Basisklasse zu haben, und es gibt keine Konvertierung zwischen ihnen. (Sie müssten einen schreiben.) Es gibt also keinen einzigen Datentyp, den bool_val ? lhs_expression : rhs_expression haben kann. Du könntest denken: "Nun, dummer Compiler, warum nicht einfach die gemeinsame Basisklasse herausfinden und verwenden?" Das ist in der Tat ein bisschen Schmerz. Wenn man das Argument, dass es richtig oder falsch ist, beiseite lässt, funktioniert das einfach nicht so.

Sie haben zwei Möglichkeiten.

One, verwenden Sie ein einfacheres Konstrukt:

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

Zwei, wenn Sie wirklich den ternären Operator verwenden möchten wirklich (was der Fall ist manchmal), müssen Sie den Compiler es Datentypen gängeln:

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) Sie können nicht benutzerdefinierte Konvertierungen zwischen Zeigertypen schreiben. (2) Sie müssen nur den Typ eines Operanden korrigieren, damit er funktioniert. (3) Sogar 'static_cast' ist Overkill, da eine implizite Konvertierung existiert, und' static_cast' könnte den Compiler daran hindern, über legitime Typsicherheitsprobleme zu warnen. –

4

Ich denke, dass die? : operator erfordert, dass die beiden Optionen vom selben Typ sind; nicht, dass sie auf die gleiche Art umgewandelt werden können

FYI nicht gcc die gleiche

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

beide Side (int) * Arbeiten Casting (aber man wusste wahrscheinlich, dass bereits)

+2

Konvertierung ist in Ordnung, aber ein Operand muss in den Typ des anderen konvertierbar sein. Wenn irgendein Typ erlaubt ist, wird das Problem im allgemeinen Fall unlösbar. –

+1

Ich denke, du hast Recht, aber es ist soooo enttäuschend! –

2

Sie beide wollen Zweige, um eine Side<int>* zurückzugeben, aber der Compiler weiß das nicht, der Typ Side<int> wird nirgendwo in diesem Ausdruck angezeigt.

Seit Ich mag auf keinen Guss verwenden, wenn eine implizite Konvertierung vorhanden ist, würde ich dies schreibe, wie:

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

Aber wenn man einen ternären Operator verwenden möchten,

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

Jetzt ist der rechte Zweig vom Typ Side<int>*, die linke Hand ist in diesen Typ konvertierbar, alles ist in Ordnung (und der Compiler optimiert die zusätzliche Variable).

+0

Grund für den Downvote? –

+0

Ich hätte nicht d/v'ed, aber ich schätze, es ist, weil Sie ein temporäres eingeführt haben - auch wenn es ein temporäres der Compiler in Dampf optimieren kann. –

0

Die beiden sollten vom gleichen Typ sein, oder einer sollte in den anderen konvertierbar sein.

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

Ein C-Style-Cast ist hier völlig übertrieben. –

+0

Und brutal hässlich und alt-out-of-date-Programmierer-hasbeen-hack-ish –

Verwandte Themen