Frage Was bedeutet das explizite Keyword?


Was macht das? explicit Stichwort Mittelwert in C ++?


2363
2017-09-23 13:58


Ursprung


Antworten:


Der Compiler darf eine implizite Konvertierung durchführen, um die Parameter in eine Funktion aufzulösen. Das bedeutet, dass der Compiler Konstruktoren verwenden kann, die mit einem Aufruf von Einzelparameter von einem Typ in einen anderen umwandeln, um den richtigen Typ für einen Parameter zu erhalten.

Hier ist eine Beispielklasse mit einem Konstruktor, der für implizite Konvertierungen verwendet werden kann:

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

Hier ist eine einfache Funktion, die ein Foo Objekt:

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

und hier ist, wo DoBar Funktion wird aufgerufen.

int main ()
{
  DoBar (42);
}

Das Argument ist nicht ein Foo Objekt, aber ein int. Es existiert jedoch ein Konstruktor für Foo das dauert ein int Mit diesem Konstruktor kann der Parameter in den richtigen Typ konvertiert werden.

Der Compiler darf dies für jeden Parameter einmal tun.

Präfixierung der explicit Schlüsselwort für den Konstruktor verhindert, dass der Compiler diesen Konstruktor für implizite Konvertierungen verwendet. Wenn Sie es der obigen Klasse hinzufügen, wird beim Funktionsaufruf ein Compilerfehler erzeugt DoBar (42). Es ist nun erforderlich, die Konvertierung explizit mit aufzurufen DoBar (Foo (42))

Der Grund, warum Sie dies tun sollten, ist eine versehentliche Konstruktion zu vermeiden, die Fehler verbergen kann. Contrived Beispiel:

  • Du hast ein MyString(int size) Klasse mit einem Konstruktor, der eine Zeichenfolge der angegebenen Größe erstellt. Du hast eine Funktion print(const MyString&)und du rufst an print(3) (wenn du tatsächlich soll anrufen print("3")). Sie erwarten, dass "3" gedruckt wird, aber stattdessen eine leere Zeichenfolge der Länge 3 gedruckt wird.

2749
2017-09-23 14:09



Angenommen, Sie haben eine Klasse String:

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

Nun, wenn Sie versuchen:

String mystring = 'x';

Der Charakter 'x' wird implizit in konvertiert int und dann die String(int) Konstruktor wird aufgerufen. Aber das ist nicht, was der Benutzer beabsichtigt haben könnte. Um solche Bedingungen zu vermeiden, definieren wir den Konstruktor als 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 ++ wird ein Konstruktor mit nur einem erforderlichen Parameter als implizite Konvertierungsfunktion betrachtet. Es konvertiert den Parametertyp in den Klassentyp. Ob das gut ist oder nicht, hängt von der Semantik des Konstruktors ab.

Zum Beispiel, wenn Sie eine String-Klasse mit Konstruktor haben String(const char* s), das ist wahrscheinlich genau das, was du willst. Sie können eine passieren const char* zu einer Funktion, die a erwartet String, und der Compiler wird automatisch eine temporäre erstellen String Objekt für dich.

Auf der anderen Seite, wenn Sie eine Pufferklasse haben, deren Konstruktor Buffer(int size) Nimmt die Größe des Puffers in Bytes an, möchten Sie den Compiler wahrscheinlich nicht ruhig schalten lassen ints hinein Buffers. Um dies zu verhindern, deklarieren Sie den Konstruktor mit dem explicit Stichwort:

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

Dieser Weg,

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

wird zu einem Fehler bei der Kompilierung. Wenn Sie eine temporäre übergeben möchten Buffer Objekt müssen Sie dies explizit tun:

useBuffer(Buffer(4));

Zusammenfassend, wenn Ihr Einzelparameterkonstruktor den Parameter in ein Objekt Ihrer Klasse konvertiert, möchten Sie wahrscheinlich nicht den explicit Stichwort. Aber wenn Sie einen Konstruktor haben, der einfach einen einzigen Parameter akzeptiert, sollten Sie ihn als deklarieren explicitum zu verhindern, dass der Compiler Sie mit unerwarteten Konvertierungen überrascht.


130
2017-10-08 14:43



Diese Antwort behandelt die Objekterstellung mit / ohne explizitem Konstruktor, da sie in den anderen Antworten nicht behandelt wird.

Betrachten Sie die folgende Klasse ohne expliziten Konstruktor:

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

private:
    int m_x;
};

Objekte der Klasse Foo können auf 2 Arten erstellt werden:

Foo bar1(10);

Foo bar2 = 20;

Abhängig von der Implementierung kann die zweite Art der Instanziierung der Klasse Foo verwirrend sein oder nicht, was der Programmierer beabsichtigt hat. Präfixierung der explicit Schlüsselwort zum Konstruktor würde einen Compiler-Fehler bei erzeugen Foo bar2 = 20;.

Es ist gewöhnlich bewährte Methode, Konstruktoren mit einem Argument als explicit, es sei denn, Ihre Implementierung verbietet dies ausdrücklich.

Beachten Sie auch, dass Konstruktoren mit

  • Standardargumente für alle Parameter, oder
  • Standardargumente für den zweiten Parameter ab

Beide können als Konstruktoren mit einem Argument verwendet werden. Vielleicht möchten Sie diese auch machen explicit.

Ein Beispiel, wenn Sie es absichtlich tun würden nicht Wenn Sie einen Funktor erstellen möchten, sollten Sie Ihren Konstruktor mit einem einzigen Argument explizit machen (sehen Sie sich die Struktur 'add_x' an, die in Dies Antworten). In diesem Fall erstellen Sie ein Objekt als add_x add30 = 30; würde wahrscheinlich Sinn machen.

Hier ist eine gute Beschreibung für explizite Konstruktoren.


34
2017-11-21 02:36



Das explicit Schlüsselwort macht einen Konvertierungskonstruktor zum Nichtkonvertierungskonstruktor. Daher ist der Code weniger fehleranfällig.


31
2017-07-10 23:48



Das Schlüsselwort explicit begleitet entweder

  • Ein Konstruktor der Klasse X, mit dem der erste (beliebige) Parameter nicht implizit in den Typ X konvertiert werden kann

C ++ [Klasse.conv.ctor]

1) Ein Konstruktor, der ohne den expliziten Funktionsspezifizierer deklariert wurde, spezifiziert eine Konvertierung von den Typen seiner Parameter in den Typ seiner Klasse. Ein solcher Konstruktor wird Konvertierungskonstruktor genannt.

2) Ein expliziter Konstruktor konstruiert Objekte genauso wie nicht-explizite Konstruktoren, aber nur dort, wo die direkte Initialisierungssyntax (8.5) oder wo Umwandlungen (5.2.9, 5.4) explizit verwendet werden. Ein Standardkonstruktor kann ein expliziter Konstruktor sein; Solch ein Konstruktor wird verwendet, um eine Standard-Initialisierung oder -Wert-Initialisierung durchzuführen   (8.5).

  • oder eine Konvertierungsfunktion, die nur für direkte Initialisierung und explizite Konvertierung in Betracht gezogen wird.

C ++ [Klasse.conv.fct]

2) Eine Konvertierungsfunktion kann explizit sein (7.1.2), in diesem Fall wird sie nur als eine benutzerdefinierte Konvertierung für die direkte Initialisierung (8.5) betrachtet. Andernfalls sind benutzerdefinierte Conversions nicht auf die Verwendung in Zuordnungen beschränkt   und Initialisierungen.

Überblick

Explizite Konvertierungsfunktionen und Konstruktoren können nur für explizite Konvertierungen (direkte Initialisierung oder explizite Umwandlungsoperation) verwendet werden, während nicht explizite Konstruktoren und Konvertierungsfunktionen sowohl für implizite als auch explizite Konvertierungen verwendet werden können.

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

Beispiel mit Strukturen X, Y, Z und Funktionen foo, bar, baz:

Lassen Sie uns einen kleinen Aufbau von Strukturen und Funktionen betrachten, um den Unterschied zu sehen explicitund nichtexplicit Konvertierungen.

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) { }

Beispiele bezüglich Konstruktor:

Konvertierung eines Funktionsarguments:

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

Objektinitialisierung:

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

Beispiele zu Konvertierungsfunktionen:

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

Konvertierung eines Funktionsarguments:

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

Objektinitialisierung:

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

Warum verwenden? explicit Konvertierungsfunktionen oder Konstruktoren?

Konvertierungskonstruktoren und nicht-explizite Konvertierungsfunktionen können Mehrdeutigkeiten einführen.

Betrachten Sie eine Struktur V, umwandelbar zu int, eine Struktur U implizit konstruierbar aus V und eine Funktion f überladen für U und bool beziehungsweise.

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

struct U { U(V) { } };

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

Ein Anruf an f ist mehrdeutig, wenn ein Objekt vom Typ übergeben wird V.

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

Der Compiler weiß nicht, ob er den Konstruktor von verwenden soll U oder die Konvertierungsfunktion zum Konvertieren der V Objekt in einen Typ für die Weitergabe an f.

Wenn entweder der Konstruktor von U oder die Umrechnungsfunktion von V wäre explicit, es gäbe keine Mehrdeutigkeit, da nur die nicht-explizite Umwandlung in Betracht gezogen würde. Wenn beide explizit der Aufruf sind f Verwenden eines Objekts vom Typ V müsste mit einer expliziten Konvertierungs- oder Cast-Operation gemacht werden.

Konvertierungskonstruktoren und nicht explizite Konvertierungsfunktionen können zu unerwartetem Verhalten führen.

Betrachten Sie eine Funktion, die einen Vektor druckt:

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

Wenn der Größenkonstruktor des Vektors nicht explizit wäre, wäre es möglich, die Funktion wie folgt aufzurufen:

print_intvector(3);

Was würde man von einem solchen Anruf erwarten? Eine Zeile enthält 3 oder drei Zeilen enthalten 0? (Wo die zweite ist, was passiert.)

Die Verwendung des expliziten Schlüsselworts in einer Klassenschnittstelle erzwingt, dass der Benutzer der Schnittstelle explizit über eine gewünschte Konvertierung spricht.

Wie Bjarne Stroustrup es (in "The C ++ Programming Language", 4. Aufl., 35.2.1, S. 1011) zur Frage warum std::duration kann nicht implizit aus einer einfachen Zahl konstruiert werden:

Wenn du weißt, was du meinst, sei klar darüber.


31
2018-05-14 09:28



Das explicit-keyword kann verwendet werden, um einen Konstruktor zu erzwingen, der aufgerufen werden soll ausdrücklich.

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

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

das explicit-Keyword vor dem Konstruktor C(void) teilt dem Compiler mit, dass nur ein expliziter Aufruf dieses Konstruktors zulässig ist.

Das explicit-keyword kann auch in benutzerdefinierten Cast-Operatoren verwendet werden:

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

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

Hier, explicit-keyword erzwingt nur explizite Umwandlungen, um gültig zu sein bool b = c; wäre in diesem Fall eine ungültige Besetzung. In Situationen wie diesen explicit- Schlüsselwort kann dem Programmierer helfen, implizite, unbeabsichtigte Umwandlungen zu vermeiden. Diese Verwendung wurde in standardisiert C ++ 11.


25
2017-10-01 22:00