Domanda

Sto scrivendo un programma con MS Visual C ++ 6.0 (sì, lo so che è antico, no non c'è niente che posso fare per aggiornare). Sto vedendo un comportamento che penso sia davvero strano. Ho una classe con due costruttori definiti in questo modo:

class MyClass
{
public:
    explicit MyClass(bool bAbsolute = true, bool bLocation = false) : m_bAbsolute(bAbsolute), m_bLocation(bLocation) { ; }
    MyClass(const RWCString& strPath, bool bLocation = false);

private:
    bool m_bAbsolute;
    bool m_bLocation;
};

Quando istanzio un'istanza di questa classe con questa sintassi: MyClass("blah"), chiama il primo costruttore. Come puoi vedere, ho aggiunto la parola chiave explicit nella speranza che non lo farebbe ... nessun dado. Sembrerebbe preferire la conversione da const char * a bool rispetto alla conversione in RWCString, che ha un costruttore di copie che accetta un strPath. Perché fa questo? Suppongo che, date due possibili scelte come questa, direbbe che è ambiguo. Cosa posso fare per impedirlo? Se possibile, vorrei evitare di dover esplicitamente lanciare l'argomento <=> su un <=>, poiché verrà usato molto con i letterali e questo è un sacco di extra digitando (più un errore davvero facile da fare).

È stato utile?

Soluzione

Esplicito non sarà di aiuto in quanto il costruttore non fa parte della conversione implicita, ma solo il destinatario.

Non c'è modo di controllare l'ordine preferito delle conversioni, ma è possibile aggiungere un secondo costruttore che ha preso un carattere const *. Per esempio:

class MyClass
{
public:
    MyClass(bool bAbsolute = true, bool bLocation = false);
    MyClass(const RWCString& strPath, bool bLocation = false);
    MyClass(const char* strPath, bool bLocation = false);

private:
    bool m_bAbsolute;
    bool m_bLocation;
};

Altri suggerimenti

Andrew Grant ha fornito la soluzione. Voglio dirti perché non funziona come hai provato. Se hai due funzioni valide per un argomento, viene chiamata quella che corrisponde meglio all'argomento. Il secondo richiede una conversione definita dall'utente, mentre il primo richiede solo una conversione standard. Ecco perché il compilatore preferisce il primo al secondo.

Se non vuoi continuare a lanciarlo, allora mi sembra che potresti dover creare un altro ctor che prende un carattere const *.

Questo è quello che probabilmente farei in questa situazione.

(Non sono sicuro del motivo per cui stai creando un ctor con un tipo che non stai passando per la maggior parte del suo utilizzo.)

modifica:

Vedo che qualcun altro ha già pubblicato questo messaggio mentre scrivevo il mio

Non sei sicuro del perché debba confondere un riferimento a una stringa e un valore booleano? Ho visto problemi con un bool e un int.
Riesci a perdere il valore predefinito per il primo costruttore - potrebbe essere che dal momento che lo sta rendendo il costruttore predefinito per MyClass (), allora è anche il valore predefinito se non può corrispondere agli args

La tua scelta è quella di aggiungere un costruttore che prende esplicitamente un carattere const *

MyClass(const char* strPath, bool bLocation = false); // Thanks Andrew Grant!

Oppure

MyClass( string("blah") );

Il compilatore sa intrinsecamente come trasformare un const char * in un bool. Dovrebbe andare a vedere se, per uno qualsiasi dei tipi del primo argomento dei costruttori MyClass, c'è un costruttore che prenderà il tipo di sorgente che gli hai dato, o se può lanciarlo in un tipo accettato da uno dei costruttori di uno qualsiasi dei tipi del primo argomento dei costruttori MyClass, o ... beh, vedi dove sta andando e questo è solo per il primo argomento. In questo modo sta la follia.

La parola chiave explicit indica al compilatore che non può convertire implicitamente un valore del tipo di argomento in un oggetto della classe, come in

struct C { explicit C( int i ): m_i(i) {}; int m_i; };
C c = 10;//disallowed
C c( 2.5 ); // allowed

C ++ ha una serie di regole per determinare quale costruttore deve essere chiamato nel tuo caso - non lo so dal retro della mia testa ma puoi intuire intuitivamente che gli argomenti predefiniti portano all'ambiguità.

Devi pensare a quelle impostazioni predefinite.

È possibile eseguire il fallback su alcuni metodi di costruzione statici denominati. Oppure puoi usare una classe diversa (che non è una cattiva scelta dal punto di vista del design). In entrambi i casi, lasci che il codice client decida quale costruttore utilizzare.

struct C {
  static C fromString( const char* s, const bool b = true );
  static C fromBools( const bool abs = true, const bool b = true );
};

o

struct CBase {
    bool absolute; 
    bool location;
    CBase( bool abs=true, bool loc=true );
};

struct CBaseFromPath {
    // makes 'absolute' true if path is absolute
    CBaseFromPath( const char* s, const bool loc );
};

Sei certo che sta davvero chiamando il primo costruttore? Lo stai chiamando con la stringa hardcoded o è nascosto dietro un #define? Sei sicuro che #define sia quello che pensi che sia? (Prova a compilare con l'opzione / Ef per ottenere l'output del preprocessore espanso e vedi se la chiamata sembra che ti aspetteresti.)

EDIT: sulla base di questo e di altri commenti, il mio suggerimento è di aggiungere un altro costruttore per const char*. È possibile?

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