Domanda Cosa significa la parola chiave esplicita?


Cosa fa il explicit parola chiave media in C ++?


2363
2017-09-23 13:58


origine


risposte:


Il compilatore può eseguire una conversione implicita per risolvere i parametri in una funzione. Ciò significa che il compilatore può utilizzare costruttori richiamabili con a parametro singolo convertire da un tipo all'altro per ottenere il tipo giusto per un parametro.

Ecco una classe di esempio con un costruttore che può essere utilizzato per le conversioni implicite:

class Foo
{
public:
  // single parameter constructor, can be used as an implicit conversion
  Foo (int foo) : m_foo (foo) 
  {
  }

  int GetFoo () { return m_foo; }

private:
  int m_foo;
};

Ecco una semplice funzione che richiede a Foo oggetto:

void DoBar (Foo foo)
{
  int i = foo.GetFoo ();
}

ed ecco dove il DoBar la funzione è chiamata.

int main ()
{
  DoBar (42);
}

L'argomento non è un Foo oggetto, ma a int. Tuttavia, esiste un costruttore per Foo ciò richiede un int quindi questo costruttore può essere usato per convertire il parametro nel tipo corretto.

Il compilatore può farlo una volta per ogni parametro.

Prefisso del explicit la parola chiave per il costruttore impedisce al compilatore di utilizzare quel costruttore per le conversioni implicite. Aggiungendolo alla classe sopra verrà creato un errore del compilatore durante la chiamata della funzione DoBar (42). Ora è necessario chiamare esplicitamente la conversione con DoBar (Foo (42))

La ragione per cui potresti voler fare questo è evitare la costruzione accidentale che può nascondere bug. Esempio di esempio

  • Hai un MyString(int size) classe con un costruttore che costruisce una stringa della dimensione data. Hai una funzione print(const MyString&)e tu chiami print(3) (quando tu in realtà destinato a chiamare print("3")). Ci si aspetta che stampi "3", ma stampa invece una stringa vuota di lunghezza 3.

2749
2017-09-23 14:09



Supponiamo, tu hai una lezione String:

class String {
public:
    String(int n); // allocate n bytes to the String object
    String(const char *p); // initializes object with char *p
};

Ora, se provi:

String mystring = 'x';

Il personaggio 'x' sarà convertito implicitamente in int e poi il String(int) il costruttore sarà chiamato. Ma questo non è ciò che l'utente potrebbe aver voluto. Quindi, per evitare tali condizioni, definiremo il costruttore come explicit:

class String {
public:
    explicit String (int n); //allocate n bytes
    String(const char *p); // initialize sobject with string p
};

962
2017-09-23 16:37



In C ++, un costruttore con un solo parametro richiesto è considerato una funzione di conversione implicita. Converte il tipo di parametro nel tipo di classe. Se questa è una cosa buona o meno dipende dalla semantica del costruttore.

Ad esempio, se hai una classe stringa con costruttore String(const char* s), probabilmente è esattamente quello che vuoi. Puoi passare a const char* a una funzione che si aspetta un Stringe il compilatore costruirà automaticamente un temporaneo String oggetto per te.

D'altra parte, se si dispone di una classe di buffer il cui costruttore Buffer(int size) prende la dimensione del buffer in byte, probabilmente non vuoi che il compilatore ruoti tranquillamente ints in BufferS. Per evitare ciò, dichiari il costruttore con il explicit parola chiave:

class Buffer { explicit Buffer(int size); ... }

Quel modo,

void useBuffer(Buffer& buf);
useBuffer(4);

diventa un errore in fase di compilazione. Se vuoi passare un temporaneo Buffer oggetto, devi farlo in modo esplicito:

useBuffer(Buffer(4));

In breve, se il costruttore di parametri singoli converte il parametro in un oggetto della classe, probabilmente non si desidera utilizzare explicit parola chiave. Ma se hai un costruttore che semplicemente prende un singolo parametro, dovresti dichiararlo come explicitper evitare che il compilatore ti sorprenda con conversioni inaspettate.


130
2017-10-08 14:43



Questa risposta riguarda la creazione di oggetti con / senza un costruttore esplicito poiché non è coperto nelle altre risposte.

Considera la seguente classe senza un costruttore esplicito:

class Foo
{
public:
    Foo(int x) : m_x(x)
    {
    }

private:
    int m_x;
};

Gli oggetti di classe Foo possono essere creati in 2 modi:

Foo bar1(10);

Foo bar2 = 20;

A seconda dell'implementazione, la seconda maniera di istanziare la classe Foo può essere confusa, o non ciò che il programmatore intendeva. Prefisso del explicit la parola chiave al costruttore genera un errore del compilatore su Foo bar2 = 20;.

È generalmente buona pratica per dichiarare i costruttori a singolo argomento come explicit, a meno che la tua implementazione non lo proibisca in modo specifico.

Si noti anche che i costruttori con

  • argomenti predefiniti per tutti i parametri o
  • argomenti predefiniti per il secondo parametro in poi

possono essere entrambi usati come costruttori a argomento singolo. Quindi potresti voler fare anche questi explicit.

Un esempio in cui vorresti deliberatamente non vuoi rendere esplicito il tuo costruttore di argomenti singoli se stai creando un functor (guarda la struct 'add_x' dichiarata in Questo risposta). In tal caso, creare un oggetto come add_x add30 = 30; avrebbe probabilmente un senso

Qui è una buona scrittura su costruttori espliciti.


34
2017-11-21 02:36



Il explicit parola chiave rende un costruttore di conversione al costruttore di non conversione. Di conseguenza, il codice è meno incline agli errori.


31
2017-07-10 23:48



La parola chiave explicit accompagna entrambi

  • un costruttore di classe X che non può essere utilizzato per convertire implicitamente il primo (qualsiasi) parametro per digitare X

C ++ [class.conv.ctor]

1) Un costruttore dichiarato senza specificatore della funzione esplicita specifica una conversione dai tipi dei suoi parametri al tipo della sua classe. Tale costruttore è chiamato costruttore di conversione.

2) Un costruttore esplicito costruisce oggetti proprio come costruttori non espliciti, ma lo fa solo dove la sintassi di inizializzazione diretta (8.5) o dove cast (5.2.9, 5.4) sono esplicitamente usati. Un costruttore predefinito può essere un costruttore esplicito; tale costruttore verrà utilizzato per eseguire l'inizializzazione di default o l'inizializzazione del valore   (8.5).

  • o una funzione di conversione che viene considerata solo per l'inizializzazione diretta e la conversione esplicita.

C ++ [class.conv.fct]

2) Una funzione di conversione può essere esplicita (7.1.2), nel qual caso viene considerata solo una conversione definita dall'utente per l'inizializzazione diretta (8.5). In caso contrario, le conversioni definite dall'utente non sono limitate all'utilizzo nelle assegnazioni   e inizializzazioni.

Panoramica

Le funzioni e i costruttori di conversione espliciti possono essere utilizzati solo per conversioni esplicite (inizializzazione diretta o operazione di cast esplicita) mentre i costruttori non espliciti e le funzioni di conversione possono essere utilizzati per conversioni implicite e esplicite.

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

Esempio utilizzando le strutture X, Y, Z e funzioni foo, bar, baz:

Diamo un'occhiata a una piccola configurazione di strutture e funzioni per vedere la differenza tra explicite non-explicit conversioni.

struct Z { };

struct X { 
  explicit X(int a); // X can be constructed from int explicitly
  explicit operator Z (); // X can be converted to Z explicitly
};

struct Y{
  Y(int a); // int can be implicitly converted to Y
  operator Z (); // Y can be implicitly converted to Z
};

void foo(X x) { }
void bar(Y y) { }
void baz(Z z) { }

Esempi relativi al costruttore:

Conversione di un argomento di funzione:

foo(2);                     // error: no implicit conversion int to X possible
foo(X(2));                  // OK: direct initialization: explicit conversion
foo(static_cast<X>(2));     // OK: explicit conversion

bar(2);                     // OK: implicit conversion via Y(int) 
bar(Y(2));                  // OK: direct initialization
bar(static_cast<Y>(2));     // OK: explicit conversion

Inizializzazione dell'oggetto:

X x2 = 2;                   // error: no implicit conversion int to X possible
X x3(2);                    // OK: direct initialization
X x4 = X(2);                // OK: direct initialization
X x5 = static_cast<X>(2);   // OK: explicit conversion 

Y y2 = 2;                   // OK: implicit conversion via Y(int)
Y y3(2);                    // OK: direct initialization
Y y4 = Y(2);                // OK: direct initialization
Y y5 = static_cast<Y>(2);   // OK: explicit conversion

Esempi relativi alle funzioni di conversione:

X x1{ 0 };
Y y1{ 0 };

Conversione di un argomento di funzione:

baz(x1);                    // error: X not implicitly convertible to Z
baz(Z(x1));                 // OK: explicit initialization
baz(static_cast<Z>(x1));    // OK: explicit conversion

baz(y1);                    // OK: implicit conversion via Y::operator Z()
baz(Z(y1));                 // OK: direct initialization
baz(static_cast<Z>(y1));    // OK: explicit conversion

Inizializzazione dell'oggetto:

Z z1 = x1;                  // error: X not implicitly convertible to Z
Z z2(x1);                   // OK: explicit initialization
Z z3 = Z(x1);               // OK: explicit initialization
Z z4 = static_cast<Z>(x1);  // OK: explicit conversion

Z z1 = y1;                  // OK: implicit conversion via Y::operator Z()
Z z2(y1);                   // OK: direct initialization
Z z3 = Z(y1);               // OK: direct initialization
Z z4 = static_cast<Z>(y1);  // OK: explicit conversion

Perché usare explicit funzioni di conversione o costruttori?

Costruttori di conversione e funzioni di conversione non esplicita possono introdurre ambiguità.

Considera una struttura V, convertibile in int, una struttura U implicitamente costruibile da V e una funzione f sovraccarico per U e bool rispettivamente.

struct V {
  operator bool() const { return true; }
};

struct U { U(V) { } };

void f(U) { }
void f(bool) {  }

Una chiamata a f è ambiguo se passa un oggetto di tipo V.

V x;
f(x);  // error: call of overloaded 'f(V&)' is ambiguous

Il compilatore non sa se usare il costruttore di U o la funzione di conversione per convertire il V oggetto in un tipo per passare a f.

Se il costruttore di U o la funzione di conversione di V sarebbe explicit, non ci sarebbe alcuna ambiguità poiché sarebbe considerata solo la conversione non esplicita. Se entrambi sono espliciti, la chiamata a f usando un oggetto di tipo V dovrebbe essere fatto usando una conversione esplicita o un'operazione di cast.

I costruttori di conversione e le funzioni di conversione non esplicita possono portare a comportamenti imprevisti.

Considera una funzione che stampa un vettore:

void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }

Se il costruttore della dimensione del vettore non fosse esplicito, sarebbe possibile chiamare la funzione in questo modo:

print_intvector(3);

Cosa ci si aspetterebbe da una tale chiamata? Una riga contenente 3 o tre righe contenenti 0? (Dove il secondo è ciò che accade.)

L'uso della parola chiave esplicita in un'interfaccia di classe fa sì che l'utente dell'interfaccia sia esplicito riguardo a una conversione desiderata.

Come dice Bjarne Stroustrup (in "The C ++ Programming Language", 4th Ed., 35.2.1, pp. 1011) sulla domanda perché std::duration non può essere costruito implicitamente da un numero semplice:

Se sai cosa intendi, sii esplicito a riguardo.


31
2018-05-14 09:28



Il explicit-keyword può essere usato per forzare un costruttore da chiamare espressamente.

class C{
public:
    explicit C(void) = default;
};

int main(void){
    C c();
    return 0;
}

il explicit-keyword davanti al costruttore C(void) dice al compilatore che è consentita solo una chiamata esplicita a questo costruttore.

Il explicit-keyword può essere utilizzato anche in operatori cast di tipo definito dall'utente:

class C{
public:
    explicit inline operator bool(void) const{
        return true;
    }
};

int main(void){
    C c;
    bool b = static_cast<bool>(c);
    return 0;
}

Qui, explicit-keyword impone che solo i cast espliciti siano validi, quindi bool b = c; sarebbe un cast non valido in questo caso. In situazioni come queste explicit-keyword può aiutare il programmatore ad evitare lanci impliciti e involontari. Questo uso è stato standardizzato in C ++ 11.


25
2017-10-01 22:00



Questo è già stato discusso (cos'è un costruttore esplicito). Ma devo dire che manca le descrizioni dettagliate trovate qui.

Inoltre, è sempre buona pratica di codifica creare i propri costruttori di argomenti (compresi quelli con valori predefiniti per arg2, arg3, ...) come già affermato. Come sempre con C ++: se non lo fai - ti auguro di averlo fatto ...

Un'altra buona pratica per le classi è rendere privata la costruzione e l'assegnazione delle copie (a.k.a. disabilitarla) a meno che non sia davvero necessario implementarla. Questo evita di avere eventuali copie di puntatori quando si usano i metodi che C ++ creerà per te di default. Un altro modo per farlo è derivato da boost :: noncopyable.


17
2017-08-20 12:45



Riferimento Cpp è sempre utile !!! È possibile trovare dettagli sullo specificatore esplicito Qui. Potrebbe essere necessario guardare conversioni implicite e copia-inizializzazione pure.

Sguardo veloce

Lo specificatore esplicito specifica che una funzione di costruzione o conversione (dal C ++ 11) non consente conversioni implicite o inizializzazione della copia.

Esempio come segue:

struct A
{
    A(int) { }      // converting constructor
    A(int, int) { } // converting constructor (C++11)
    operator bool() const { return true; }
};

struct B
{
    explicit B(int) { }
    explicit B(int, int) { }
    explicit operator bool() const { return true; }
};

int main()
{
    A a1 = 1;      // OK: copy-initialization selects A::A(int)
    A a2(2);       // OK: direct-initialization selects A::A(int)
    A a3 {4, 5};   // OK: direct-list-initialization selects A::A(int, int)
    A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
    A a5 = (A)1;   // OK: explicit cast performs static_cast
    if (a1) cout << "true" << endl; // OK: A::operator bool()
    bool na1 = a1; // OK: copy-initialization selects A::operator bool()
    bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization

//  B b1 = 1;      // error: copy-initialization does not consider B::B(int)
    B b2(2);       // OK: direct-initialization selects B::B(int)
    B b3 {4, 5};   // OK: direct-list-initialization selects B::B(int, int)
//  B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int)
    B b5 = (B)1;   // OK: explicit cast performs static_cast
    if (b5) cout << "true" << endl; // OK: B::operator bool()
//  bool nb1 = b2; // error: copy-initialization does not consider B::operator bool()
    bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization
}

15
2017-12-20 12:19



Costruttori di conversioni esplicite (solo C ++)

Lo specificatore di funzione esplicita controlla il tipo implicito indesiderato   conversioni. Può essere utilizzato solo in dichiarazioni di costruttori   all'interno di una dichiarazione di classe. Ad esempio, ad eccezione del valore predefinito   costruttore, i costruttori nella seguente classe sono la conversione   costruttori.

class A
{
public:
    A();
    A(int);
    A(const char*, int = 0);
};

Le seguenti dichiarazioni sono legali:

A c = 1;
A d = "Venditti";

La prima dichiarazione è equivalente a A c = A( 1 );.

Se dichiari il costruttore della classe come explicit, le precedenti dichiarazioni sarebbero illegali.

Ad esempio, se dichiari la classe come:

class A
{
public:
    explicit A();
    explicit A(int);
    explicit A(const char*, int = 0);
};

È possibile solo assegnare valori che corrispondono ai valori del tipo di classe.

Ad esempio, le seguenti dichiarazioni sono legali:

  A a1;
  A a2 = A(1);
  A a3(1);
  A a4 = A("Venditti");
  A* p = new A(1);
  A a5 = (A)1;
  A a6 = static_cast<A>(1);

9
2018-01-23 09:26