2015-04-24 46 views
14

Questo codice:tipi di operando incompatibili quando si utilizza ternario operatore condizionale

bool contains = std::find(indexes.begin(), indexes.end(), i) != indexes.end(); 
    CardAbility* cardAbility = contains ? new CardAbilityBurn(i) : new CardAbilityEmpty; 

mi dà il seguente errore:

Incompatible operand types CardAbilityBurn and CardAbilityEmpty

Tuttavia se scrivo il codice come questo:

if (contains) 
{ 
    cardAbility = new CardAbilityBurn(i); 
} 
else 
{ 
    cardAbility = new CardAbilityEmpty; 
} 

quindi il compilatore non si preoccupa. Perchè così? Voglio usare l'operatore condizionale ternario perché è solo una riga. Cosa c'è che non va?

Ho bisogno di notare (penso che potrebbe essere necessario questa informazione) che CardAbilityEmpty e CardAbilityBurn entrambi derivano da CardAbility quindi sono per così dire fratelli.

Grazie

+3

Essenzialmente lo stesso problema di http://stackoverflow.com/questions/24706480. La soluzione più semplice è convertire esplicitamente uno di questi in 'CardAbility *'. –

+0

Domanda davvero interessante. –

+0

Quale compilatore usi? Su VS2008 funziona con classi derivate. –

risposta

14

Il tipo di sistema di C++ determina i tipi di espressioni dall'interno all'esterno [1]. Ciò significa che il tipo dell'espressione condizionale viene determinato prima dell'assegnazione a CardAbility* e che il compilatore deve scegliere solo con CardAbilityBurn* e CardAbilityEmpty*.

Poiché C++ presenta ereditarietà multiple e alcuni percorsi di conversione più possibili, poiché nessuno dei tipi è una superclasse dell'altro, la compilazione si ferma qui.

Per compilare correttamente, è necessario fornire la parte mancante: eseguire il cast di uno o entrambi gli operandi sul tipo di classe base, in modo che l'espressione condizionale nel suo complesso possa prendere quel tipo.

auto* cardAbility = contains 
    ? static_cast<CardAbility*>(new CardAbilityBurn(i)) 
    : static_cast<CardAbility*>(new CardAbilityEmpty ); 

(Si noti l'uso di auto, dal momento che già fornito il tipo di destinazione nell'espressione destra.)

E è però un po 'contorto, quindi alla fine del if-else la struttura è più adatta in questo caso.

[1] C'è un'eccezione: i nomi di funzione sovraccaricati non hanno un tipo definitivo finché non li converti (implicitamente o esplicitamente) in una delle loro versioni.

2

Ci sono several cases described for Microsoft compilers, come gestire tipi di operando.

If both operands are of the same type, the result is of that type.

If both operands are of arithmetic or enumeration types, the usual arithmetic conversions (covered in Arithmetic Conversions) are performed to convert them to a common type.

If both operands are of pointer types or if one is a pointer type and the other is a constant expression that evaluates to 0, pointer conversions are performed to convert them to a common type.

If both operands are of reference types, reference conversions are performed to convert them to a common type.

If both operands are of type void, the common type is type void.

If both operands are of the same user-defined type, the common type is that type.

If the operands have different types and at least one of the operands has user-defined type then the language rules are used to determine the common type. (See warning below.)

E poi c'è un avvertimento:

If the types of the second and third operands are not identical, then complex type conversion rules, as specified in the C++ Standard, are invoked. These conversions may lead to unexpected behavior including construction and destruction of temporary objects. For this reason, we strongly advise you to either (1) avoid using user-defined types as operands with the conditional operator or (2) if you do use user-defined types, then explicitly cast each operand to a common type.

Probabilmente, questa è la ragione, Apple ha disattivato questa conversione implicita in LLVM.

Quindi, se/else sembra essere più appropriato nel tuo caso.

+0

Non sta usando gli operandi di qualche tipo definito dall'utente. Sta usando gli operandi che sono puntatori. Non ci sarà conversione di oggetti. Ci sarebbe al massimo una conversione di puntatori a un tipo diverso, che il compilatore quindi rifiuta di fare. Non converte due puntatori oggetto in un puntatore a una superclasse comune. – gnasher729

+0

Viene applicato il terzo caso di utilizzo nei compilatori MS: conversione su puntatori, che potrebbero non essere implementati in LLVM. Nulla è detto sulla conversione di oggetti. –

Problemi correlati