Si basa sulla conversione implicita degli argomenti considerata pericolosa?

10

C ++ ha una funzione (non riesco a capire il nome corretto di essa), che chiama automaticamente i costruttori corrispondenti dei tipi di parametro se i tipi di argomento non sono quelli attesi.

Un esempio molto semplice di ciò è chiamare una funzione che si aspetta un std::string con un argomento const char* . Il compilatore genererà automaticamente il codice per richiamare il costruttore std::string appropriato.

Mi sto chiedendo, è pessimo per la leggibilità, come penso che sia?

Ecco un esempio:

class Texture {
public:
    Texture(const std::string& imageFile);
};

class Renderer {
public:
    void Draw(const Texture& texture);
};

Renderer renderer;
std::string path = "foo.png";
renderer.Draw(path);

Va bene? O va troppo lontano? Se non dovessi farlo, posso in qualche modo far avvertire Clang o GCC?

    
posta futlib 15.03.2013 - 16:51
fonte

3 risposte

24

Questo è indicato come un costruttore di conversione (o talvolta un costruttore implicito o una conversione implicita).

Non sono a conoscenza di un passaggio in fase di compilazione per avvisare quando ciò si verifica, ma è molto facile da prevenire; usa solo la parola chiave explicit .

class Texture {
public:
    explicit Texture(const std::string& imageFile);
};

Riguardo alla possibilità o meno di convertire i costruttori è una buona idea: dipende.

Circostanze in cui la conversione implicita ha senso:

  • La classe è abbastanza economica da costruire che non ti interessa se è costruita implicitamente.
  • Alcune classi sono concettualmente simili ai loro argomenti (come std::string che riflette lo stesso concetto di const char * da cui è implicitamente possibile convertire), quindi la conversione implicita ha senso.
  • Alcune classi diventano molto più spiacevoli da utilizzare se la conversione implicita è disabilitata. (Pensa di dover invocare esplicitamente std :: string ogni volta che vuoi passare una stringa letterale. Le parti di Boost sono simili.)

Circostanze in cui la conversione implicita ha meno senso:

  • La costruzione è costosa (come il tuo esempio di Texture, che richiede il caricamento e l'analisi di un file grafico).
  • Le lezioni sono concettualmente molto dissimili dai loro argomenti. Si consideri, ad esempio, un contenitore di tipo array che prende le sue dimensioni come argomento:
    class FlagList
    {
        FlagList(int initial_size); 
    };

    void SetFlags(const FlagList& flag_list);

    int main() {
        // Now this compiles, even though it's not at all obvious
        // what it's doing.
        SetFlags(42);
    }
  • La costruzione potrebbe avere effetti collaterali indesiderati. Ad esempio, una classe AnsiString dovrebbe not implicitamente costruire da UnicodeString , poiché la conversione da Unicode a ANSI potrebbe perdere informazioni.

Ulteriori letture:

risposta data 15.03.2013 - 18:09
fonte
3

Questo è più un commento che una risposta, ma troppo grande per inserire un commento.

È interessante notare che g++ non mi consente di farlo:

#include <iostream>
#include <string>

class Texture {
        public:
                Texture(const std::string& imageFile)
                {
                        std::cout << "Texture()" << std::endl;
                }
};

class Renderer {
        public:
                void Draw(const Texture& texture)
                {
                        std::cout << "Renderer.Draw()" << std::endl;
                }
};

int main(int argc, char* argv[])
{
        Renderer renderer;
        renderer.Draw("foo.png");

        return 0;
}

Produce quanto segue:

$ g++ -o Conversion.exe Conversion.cpp 
Conversion.cpp: In function ‘int main(int, char**)’:
Conversion.cpp:23:25: error: no matching function for call to ‘Renderer::Draw(const char [8])’
Conversion.cpp:14:8: note: candidate is: void Renderer::Draw(const Texture&)

Tuttavia, se cambio la linea in:

   renderer.Draw(std::string("foo.png"));

Effettuerà questa conversione.

    
risposta data 15.03.2013 - 17:38
fonte
3

Si chiama conversione di tipo implicita. In generale è una buona cosa, poiché inibisce la ripetizione inutile. Ad esempio, ottieni automaticamente una versione std::string di Draw senza dover scrivere alcun codice aggiuntivo. Può anche aiutare a seguire il principio di open-closed, poiché ti consente di estendere le capacità di Renderer senza modificare Renderer stesso.

D'altra parte, non è privo di inconvenienti. Può essere difficile capire da dove viene una discussione, per prima cosa. A volte può produrre risultati imprevisti in altri casi. Ecco a cosa serve la parola chiave explicit . Se lo metti nel costruttore Texture , disabilita l'utilizzo di quel costruttore per la conversione del tipo implicito. Non sono a conoscenza di un metodo per l'avvertimento globale sulla conversione di tipo implicita, ma ciò non significa che un metodo non esiste, solo che gcc ha un numero incomprensibilmente grande di opzioni.

    
risposta data 15.03.2013 - 17:50
fonte