2015-06-22 10 views
13

Ho una classe a portata di mano in C++ che può essere inizializzata con uno double o con uno char*. Tutto funziona come previsto tranne un caso quando l'argomento è zero.Zero in double vs char * ambiguity

struct Var { 
    Var (double src) {} 
    Var (char *src) {} 
}; 

int main(int argc, char* argv[]) { 
    Var A = 123; 
    Var B = "abc"; 
    Var C = 0; <- Error: conversion from 'int' to 'Var' is ambiguous 
} 

In precedenza ho usato int invece di double, e andava bene per qualche motivo.

Come può essere risolto?

PS: So che posso usare (double)0 o semplicemente 0.0, ma c'è un modo per consentire solo 0 per essere accettato come doppia?

+6

Hai provato ad aggiungere un costruttore che accetta un 'int', chiamando il' double'-costruttore? – Caramiriel

+2

In 'C++' il '0' può essere un * intero zero * o un * puntatore nullo *. Il recente standard 'C++ 11' si rivolge a questo introducendo la parola chiave' nullptr' per il * puntatore nullo *. – Galik

+1

@Galik: Sort of. È ancora vero che '0' è ambiguo. –

risposta

11

Si potrebbe eliminare l'ambiguità con l'aggiunta di un terzo costruttore che prende un int e delega alla versione double (richiede C++ 11):

struct Var { 
    Var (double src) {} 
    Var (const char *src) {} 
    // ^^^^^ should be const 
    Var (int src) : Var {static_cast<double>(src)} {} 
}; 

Tuttavia, questo potrebbero essere risolti più facilmente cambiando la tua char* costruttore per prendere un std::string invece e utilizzando l'inizializzazione diretta.

+0

Beh, chissà se usare 'std :: string' è accettabile ... La prima alternativa è sicuramente buona, se è così che dovrebbe essere risolta. – Deduplicator

7

In C++, il valore letterale 0 ha tipo int. Non puoi cambiare nulla essendo un numero intero, senza aggiungere almeno un punto decimale. (Senza effettiva necessità di scrivere cifre dopo il punto però)

di rispondergli domanda implicita per quanto riguarda il motivo per cui la chiamata era inequivocabile con 123 ma ambiguo con 0:

  • durante l'inizializzazione A, andava bene perché non c'è conversione implicita da un int a un puntatore (char*, in questo caso).

  • durante l'inizializzazione C utilizzando un int la seconda volta, è stato utilizzato 0, letterale 0 può essere implicitamente convertito in un puntatore (un comportamento legacy, prima di C++ 11 offerto nullptr letterale).

Quindi, in poche parole:

int * a = 0; // legal; 
int * b = 2; // ILLEGAL; 

Quando si inizializza C una conversione è necessario per dare l'argomento qualsiasi due costruttori disponibili (ed entrambe le conversioni hanno la stessa classifica), la chiamata è ambiguo.

Per rendere inequivocabile, è necessario eseguire il cast del letterale int a un numero doppio ((double)0), utilizzare un valore letterale doppio (0.) o aggiungere un costruttore che acquisisce un int. Con questa ultima soluzione, il costruttore che prende un int è una corrispondenza esatta, quindi è meglio che un altro sovraccarico richieda una conversione (motivo per cui sarebbe stato selezionato).

Modifica. Si prega di notare che la risposta TartanLlama dovrebbe essere accettata qui in quanto è una pratica del C++ migliore: il costruttore che prende una stringa in stile C dovrebbe prendere invece un std::string.Poiché non v'è una conversione implicita da stringa in stile C per std::string, la chiamata funziona con pochi cambiamenti:

Var B("abc"); 

Ma si isolerà l'interfaccia classe da puntatori dettagli.

+2

Per "Lo 0 letterale ha tipo int". E il puntatore nullo? E pure funzioni virtuali? –

+2

@PeterMortensen: il puntatore nullo comporta una conversione, quindi è classificato peggiore durante la risoluzione di sovraccarico rispetto a una corrispondenza diretta con 'int'. Pure le funzioni virtuali non hanno nulla a che fare con un '0' letterale, la grammatica non accetta un letterale lì, solo la sequenza token' = 0'. (Ad esempio, '0x0' è anche uno zero letterale intero e converte anche in un puntatore nullo, ma non è accettabile in un puro specificatore) –