Domanda

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

Nell'esempio sopra, perché test è uguale a 6 (anziché 6.1) se some_condition è vero?

La modifica del codice come di seguito restituisce un valore di 6.1

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

Sembra che (nell'esempio originale) il valore restituito da Foo :: operator double venga castato su un int e poi di nuovo su un double. Perché?

È stato utile?

Soluzione

L'operatore condizionale controlla le conversioni in entrambe le direzioni. In questo caso, poiché il tuo costruttore è esplicito (quindi ?: non è ambiguo), viene utilizzata la conversione da Foo a int , usando la tua funzione di conversione che converte in double : funziona, perché dopo aver applicato la funzione di conversione, una conversione standard che converte il double in int (troncamento ) segue. Il risultato di ?: nel tuo caso è int e ha il valore 6 .

Nel secondo caso, poiché l'operando ha il tipo double , non ha luogo tale conversione finale in int , e quindi il tipo di risultato di ?: ha il tipo double con il valore previsto.

Per capire il "non necessario" conversioni, devi capire che espressioni come il tuo ?: vengono valutate " senza contesto " ;: Nel determinare il valore e il tipo di esso, il compilatore non considera che è l'operando di un < code> return per una funzione che restituisce un double .


Modifica: cosa succede se il costruttore è implicito ? L'espressione ?: sarà ambigua, perché puoi convertire un int in un valore di tipo Foo (usando il costruttore) e un < code> Foo su un valore di tipo int (utilizzando la funzione di conversione). Lo standard dice

  

Utilizzando questo processo, viene determinato se il secondo operando può essere convertito in modo che corrisponda al terzo operando e se il terzo operando può essere convertito in modo che corrisponda al secondo operando. Se entrambi possono essere convertiti o uno può essere convertito ma la conversione è ambigua, il programma è mal formato.


Paragrafi che spiegano come il tuo Foo viene convertito in int :

5.16 / 3 sulla condizione ? E1: E2 :

  

Altrimenti, se il secondo e il terzo operando hanno tipi diversi e uno dei due ha un tipo di classe (possibilmente qualificato in cv), si tenta di convertire ciascuno di questi operandi nel tipo dell'altro. [...] E1 può essere convertito in corrispondenza di E2 se E1 può essere implicitamente convertito nel tipo che avrebbe l'espressione E2 se E2 fosse convertito in un valore (o il tipo che ha, se E2 è un valore).

4.3 su " implicitamente convertito " ;:

  

Un'espressione e può essere implicitamente convertita in un tipo T se e solo se la dichiarazione T t = e; è ben formata, per alcune variabili temporanee inventate t.

8.5 / 14 sull'inizializzazione della copia ( T t = e; )

  

Se il tipo di origine è un tipo di classe (possibilmente qualificato in cv), vengono considerate le funzioni di conversione. Le funzioni di conversione applicabili sono elencate (13.3.1.5) e la migliore viene scelta tramite la risoluzione di sovraccarico (13.3). La conversione definita dall'utente così selezionata viene chiamata per convertire l'espressione dell'inizializzatore nell'oggetto che viene inizializzato. Se la conversione non può essere eseguita o è ambigua, l'inizializzazione non è corretta.

13.3.1.5 relativo ai candidati alla funzione di conversione

  

Sono considerate le funzioni di conversione di S e delle sue classi base. Quelli che non sono nascosti in S e producono il tipo T o un tipo che può essere convertito in tipo T tramite una sequenza di conversione standard (13.3.3.1.1) sono funzioni candidate.

Altri suggerimenti

Questo è trattato in dettaglio positivamente confuso nella sezione 5.16 della norma. La parte importante è nel paragrafo 3. "Se E2 è un lvalue: E1 può essere convertito per abbinare E2 se E1 può essere convertito implicitamente (clausola 4) nel tipo" riferimento a T2 ", fatto salvo il vincolo che nella conversione il riferimento deve legare direttamente (8.5.3) a E1. "

Nell'espressione, l'unico valore è item , quindi la domanda è se 0 (un int) può essere implicitamente convertito nel tipo Foo . In questo caso, non esiste una conversione implicita di nessun altro tipo in Foo , poiché l'unica funzione di conversione disponibile è contrassegnata con esplicito . Pertanto, ciò non funziona e seguiamo con " se E2 è un valore, o se la conversione sopra non può essere effettuata: " (saltando la parte se entrambi hanno un tipo di classe) " Altrimenti (cioè, se E1 o E2 ha un tipo non di classe, o se entrambi hanno tipi di classe ma le classi sottostanti non sono né uguali né una classe base del altro): E1 può essere convertito in corrispondenza di E1 se E1 può essere implicitamente convertito nel tipo che avrebbe l'espressione E2 se E2 fosse convertito in un valore (o il tipo che ha, se E2 è un valore). "

Pertanto, vediamo che 0 è un valore, di tipo int . Possiamo convertire un Foo , dal momento che possiamo implicitamente convertire un Foo in un double e quindi in un int . Poi:

" Utilizzando questo processo, viene determinato se il secondo operando può essere convertito per corrispondere al terzo operando e se il terzo operando può essere convertito per corrispondere al secondo operando. Se entrambi possono essere convertiti, o uno può essere convertito ma la conversione è ambigua, il programma è mal formato. Se nessuno dei due può essere convertito, gli operandi rimangono invariati e vengono eseguiti ulteriori controlli come descritto di seguito. Se è possibile esattamente una conversione, tale conversione viene applicata all'operando scelto e l'operando convertito viene utilizzato al posto dell'operando originale per il resto di questa sezione. & Quot;

Poiché possiamo convertire un Foo in un int , convertiamo il Foo in un int per il resto della determinazione. Ora abbiamo due int come tipi di espressione e almeno uno è un valore.

Posso continuare con i paragrafi 5 e 6, ma penso sia abbastanza ovvio che l'espressione ha il tipo int .

Penso che i takeaway siano:

  1. Il tuo compilatore funziona secondo lo standard.

  2. Le regole sul tipo di espressione condizionale sono troppo complicate per essere facilmente apprese. Non spingere la busta, perché prima o poi sbaglierai. (Inoltre, questo è esattamente il tipo di posto in cui un compilatore potrebbe non riuscire a implementare esattamente lo standard.)

  3. Prova a specificare i tipi in modo che sia la seconda che la terza espressione siano dello stesso tipo. In ogni caso, cerca di evitare espressioni che non sono del tipo desiderato.

Il tipo dell'espressione ternaria è determinato in fase di compilazione; non importa quale some_condition sia in fase di esecuzione.

Immagino che la domanda sia allora: perché il compilatore sceglie int invece di raddoppiare nel primo esempio?

L'operatore ternario indovina il tipo dai suoi argomenti. non può convertire l'oggetto in int ma può convertire l'oggetto in doppio, che poi converte in int.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top