Frage

Was bedeutet das explicit Schlüsselwort bedeuten in C++?

War es hilfreich?

Lösung

Der Compiler erlaubt eine implizite Konvertierung zu machen, um die Parameter an eine Funktion zu lösen. Das bedeutet, dass der Compiler Konstrukteurs aufrufbar mit einem einzelnen Parameter verwenden können von einem Typ in einen anderen zu konvertieren, um die richtige Art für einen Parameter zu erhalten.

Hier ist ein Beispiel-Klasse mit einem Konstruktor, der für implizite Konvertierungen verwendet werden können:

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 eine Foo Aufgabe übernimmt:

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

und hier ist, wo die DoBar Funktion aufgerufen wird.

int main ()
{
  DoBar (42);
}

Das Argument ist kein Foo Objekt, sondern ein int. Jedoch gibt es einen Konstruktor für Foo, die eine int nimmt so dieser Konstruktor verwendet werden kann, um den Parameter auf den richtigen Typ zu konvertieren.

Der Compiler erlaubt ist, diese für jeden Parameter einmal zu tun.

Voranstellen des explicit Schlüsselwort an den Konstruktor verhindert, dass der Compiler aus, dass Konstruktor für implizite Konvertierungen verwenden. Zugabe zu der obigen Klasse erstellen einen Compiler-Fehler bei dem Funktionsaufruf DoBar (42). Es ist nun notwendig für die Konvertierung mit DoBar (Foo (42)) explizit aufrufen

Der Grund, warum Sie wollen könnte, dies zu tun ist zufällig Konstruktion zu vermeiden, die Wanzen verstecken können. Konstruiertes Beispiel:

  • Sie haben eine MyString(int size) Klasse mit einem Konstruktor, der einen String der angegebenen Größe konstruiert. Sie haben eine Funktion print(const MyString&), und rufen Sie print(3) (wenn Sie wirklich soll print("3") nennen). Sie erwarten, dass es „3“ zu drucken, aber er druckt eine leere Zeichenfolge der Länge 3 statt.

Andere Tipps

Nehmen wir an, 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
};

Wenn Sie nun versuchen:

String mystring = 'x';

Das Zeichen 'x' implizit konvertiert werden int und dann wird der String(int) Konstruktor aufgerufen werden. Aber dies ist nicht das, was der Benutzer gedacht haben könnte. Also, um solche Bedingungen zu vermeiden, werden wir den Konstruktor als explicit definieren:

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

In C ++ Konstruktor mit nur einem gewünschten Parameter ist eine implizite Konvertierungsfunktion betrachtet. Er wandelt den Parametertyp auf den Klassentyp. Ob dies eine gute Sache ist oder nicht, hängt von der Semantik des Konstrukteurs.

Zum Beispiel, wenn Sie eine String-Klasse mit Konstruktor String(const char* s) haben, ist das wahrscheinlich genau das, was Sie wollen. Sie können eine const char* an eine Funktion einen String erwartet, und der Compiler ein temporäres String Objekt automatisch für Sie erstellen.

Auf der anderen Seite, wenn Sie einen Puffer Klasse, deren Konstruktor Buffer(int size) haben nimmt die Größe des Puffers in Bytes, Sie wollen wahrscheinlich nicht der Compiler zu leise ints in Buffers drehen. Um zu verhindern, dass Sie den Konstruktor mit dem explicit Schlüsselwort deklarieren:

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

Auf diese Weise

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

wird ein Fehler bei der Kompilierung. Wenn Sie ein temporäres Buffer Objekt zu übergeben mögen, müssen Sie dies ausdrücklich tun:

useBuffer(Buffer(4));

Zusammenfassend: Wenn Ihr Einzelparameter-Konstruktor die Parameter in ein Objekt der Klasse konvertiert, Sie wollen wahrscheinlich nicht das explicit Schlüsselwort verwenden. Aber wenn Sie einen Konstruktor haben, der einfach einen einzelnen Parameter nehmen geschieht, sollten Sie es als explicit erklären den Compiler aus überraschen Sie mit unerwarteten Konvertierungen zu verhindern.

Diese Antwort ist über die Objekterstellung mit / ohne einen expliziten Konstruktor, da sie nicht in den anderen Antworten abgedeckt ist.

Betrachten Sie die folgende Klasse ohne einen expliziten Konstruktor:

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

private:
    int m_x;
};

Objekte der Klasse Foo kann auf 2 Arten erstellt werden:

Foo bar1(10);

Foo bar2 = 20;

Abhängig von der Implementierung, die zweite Art Klasse Foo instanziieren kann verwirrend sein, oder nicht, was der Programmierer gedacht. Voranstellen des explicit Schlüsselwort an den Konstruktor würde einen Compiler-Fehler bei Foo bar2 = 20; erzeugen.

Es ist in der Regel gute Praxis Single-Argument Bauer als explicit zu erklären, es sei denn, Ihre Implementierung es ausdrücklich verbietet.

Beachten Sie auch, dass Konstrukteure mit

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

kann sowohl als Einzel Argument Konstrukteuren verwendet werden. So möchten Sie können diese auch explicit machen.

Ein Beispiel, wenn Sie bewusst würde nicht Möchten Sie Ihre Single-Argument Konstruktor explizit machen, wenn Sie einen Funktor bist zu schaffen (Blick auf die ‚ADD_X‘ erklärt struct in diese beantworten). In einem solchen Fall würde ein Objekt als add_x add30 = 30; Schaffung wahrscheinlich Sinn machen.

Hier ist eine gute schreib- up auf explizite Konstrukteuren.

Das explicit Schlüsselwort macht einen Konvertierungskonstruktor zu nicht-Konvertierungskonstruktor. Als Ergebnis ist der Code weniger fehleranfällig.

Das Schlüsselwort explicit begleitet entweder

  • ein Konstruktor der Klasse X, der nicht zum impliziten Konvertieren des ersten (einzigen) Parameters in den Typ X verwendet werden kann

C++ [class.conv.ctor]

1) Ein Konstruktor, der ohne den expliziten Funktionsbezeichner deklariert wurde, gibt eine Konvertierung von den Typen seiner Parameter in den Typ seiner Klasse an.Ein solcher Konstruktor wird als Konvertierungskonstruktor bezeichnet.

2) Ein expliziter Konstruktor erstellt Objekte genau wie nicht explizite Konstruktoren, tut dies jedoch nur, wenn die Direktinitialisierungssyntax (8.5) oder Umwandlungen (5.2.9, 5.4) explizit verwendet werden.Ein Standardkonstruktor kann ein expliziter Konstruktor sein;Ein solcher Konstruktor wird verwendet, um eine Standardinitialisierung oder ValueInitialisierung durchzuführen (8.5).

  • oder eine Konvertierungsfunktion, die nur für die direkte Initialisierung und explizite Konvertierung berücksichtigt wird.

C++ [class.conv.fct]

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

Ü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 für 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:

Schauen wir uns einen kleinen Aufbau von Strukturen und Funktionen an, um den Unterschied zwischen ihnen zu erkennen explicit und nicht-explicit 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 zum 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 zu Mehrdeutigkeiten führen.

Betrachten Sie eine Struktur V, umwandelbar in int, eine Struktur U implizit konstruierbar aus V und eine Funktion f überlastet für U Und bool jeweils.

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

struct U { U(V) { } };

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

Ein Aufruf 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 V Objekt in einen Typ zur Übergabe an f.

Wenn entweder der Konstruktor von U oder die Konvertierungsfunktion von V wäre explicit, gäbe es keine Mehrdeutigkeit, da nur die nicht explizite Konvertierung berücksichtigt würde.Wenn beide explizit sind, erfolgt der Aufruf von f Verwenden eines Objekts vom Typ V müsste mithilfe einer expliziten Konvertierungs- oder Umwandlungsoperation erfolgen.

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

Stellen Sie sich eine Funktion vor, 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 mit 3 oder drei Zeilen mit 0?(Wobei das zweite ist, was passiert.)

Die Verwendung des Schlüsselworts „explicit“ in einer Klassenschnittstelle zwingt den Benutzer der Schnittstelle dazu, eine gewünschte Konvertierung explizit anzugeben.

Wie Bjarne Stroustrup es ausdrückt (in „The C++ Programming Language“, 4. Auflage, 35.2.1, S.1011) zur Frage nach dem Warum std::duration kann nicht implizit aus einer einfachen Zahl konstruiert werden:

Wenn Sie wissen, was Sie meinen, machen Sie es deutlich.

Explizite Umwandlung Bauer (nur C ++)

  

Die explizite Funktion Bezeichner steuert unerwünschten impliziten Typen   Konvertierungen. Es kann nur in Erklärungen von Konstrukteuren verwendet werden   innerhalb einer Klassendeklaration. Zum Beispiel, mit Ausnahme des Standard   Konstruktor, die Konstrukteure in der folgenden Klasse sind Umwandlung   Konstrukteure.

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

Die folgenden Erklärungen sind zulässig:

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

Die erste Deklaration entspricht A c = A( 1 );.

Wenn Sie den Konstruktor der Klasse als explicit erklären, würden die bisherigen Erklärungen illegal sein.

Zum Beispiel, wenn Sie deklarieren die Klasse als:

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

Sie können nur Werte zuweisen, die die Werte des Klassentypen entsprechen.

Zum Beispiel sind die folgenden Anweisungen zulässig:

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

Das explicit-Schlüsselwort verwendet werden kann, einen Konstruktor zu erzwingen aufgerufen werden explizit .

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

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

das explicit-Schlüsselwort vor dem Konstruktor C(void) teilt dem Compiler mit, dass nur expliziten Aufruf dieser Konstruktor zulässig ist.

Das explicit-Schlüsselwort kann auch in benutzerdefinierten Typ 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-Schlüsselwort erzwingt nur explizites Casts gültig sein, so würde bool b = c; eine ungültige Besetzung in diesem Fall sein. In Situationen wie diese explicit-Schlüsselwort kann Programmierer helfen implizit, unbeabsichtigte Abgüsse zu vermeiden. Diese Nutzung hat in C ++ 11 standardisiert.

Cpp Referenz ist immer hilfreich !!! Einzelheiten zu den expliziten Spezifizierer können hier . //en.cppreference: Sie können unter impliziten Konvertierungen und copy-Initialisierung zu.

Quick look

  

Die expliziten Spezifizierer spezifizieren, daß ein Konstruktor oder Umwandlungsfunktion (da C ++ 11) erlaubt keine impliziten Konvertierungen oder Kopieren-Initialisierung.

Beispiel wie folgt:

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
}

Dies wird bereits diskutiert ( was expliziter Konstruktor ist). Aber ich muss sagen, dass es die detaillierten Beschreibungen hier fehlt.

Außerdem ist es immer eine gute Codierung der Praxis Dir ein Argument Konstrukteuren zu machen (auch mit Standardwerten für arg2, arg3, ...), wie bereits erwähnt. Wie immer mit C ++: wenn Sie nicht - Sie werden sich wünschen, du hast ...

Eine weitere gute Praxis für die Klassen zu kopieren Aufbau und Zuordnung privat (auch bekannt als deaktivieren), wenn Sie wirklich brauchen, zu implementieren, es zu machen. Dies vermeidet eventuelle Kopien von Zeigern, wenn die Methoden verwenden, die C ++ für Sie standardmäßig schaffen. Eine andere Möglichkeit, dies zu tun, ist von boost :: ableiten noncopyable.

Konstrukteurs anhängen implizite Konvertierung. Zur Unterdrückung dieser implizite Konvertierung ist es erforderlich, einen Konstruktor mit einem Parameter explizit zu erklären.

In C ++ 11 können Sie auch einen "Operator-Typ ()" mit einem solchen Stichwort http://en.cppreference.com/w/cpp/language/explicit Mit einer solchen Spezifikation Sie Betreiber in Bezug auf explizite Konvertierungen und direkte Initialisierung des Objekts verwenden können.

P. S. Wenn Transformationen definiert durch Benutzer unter Verwendung von (über Konstruktoren und Typ-Umwandlungsoperator) wird nur verwendet, eine Ebene der impliziten Konvertierungen erlaubt. Aber Sie können diese Umwandlungen mit anderen Sprachkonvertierungen kombinieren

  • up Integral Reihen (char in int, um Doppel float);
  • standart Conversions (int zu verdoppeln);
  • konvertiert Zeiger von Objekten Basisklasse und für ungültig zu erklären *;
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top