parameter - c++ schlüsselwörter bedeutung




Was bedeutet das explizite Keyword? (8)

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


Das explicit Schlüsselwort wird entweder mitgeliefert

  • ein Konstruktor der Klasse X, der nicht verwendet werden kann, um den ersten (nur beliebigen) Parameter implizit in den Typ X zu konvertieren

C ++ [class.conv.ctor]

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

2) Ein expliziter Konstruktor erstellt Objekte genauso wie nicht explizite Konstruktoren, dies jedoch nur, wenn die Direktinitialisierungssyntax (8.5) oder die Umwandlung (5.2.9, 5.4) explizit verwendet wird. Ein Standardkonstruktor kann ein expliziter Konstruktor sein. Ein solcher Konstruktor wird zur Ausführung der Standardinitialisierung oder Wertinitialisierung (8.5) verwendet.

  • 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 Direktinitialisierung (8.5) betrachtet. Andernfalls sind benutzerdefinierte Konvertierungen nicht auf Zuweisungen und Initialisierungen beschränkt.

Überblick

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

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

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

Lassen Sie uns einen kleinen Aufbau von Strukturen und Funktionen betrachten, um den Unterschied zwischen explicit und nicht explicit Konvertierungen zu erkennen.

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 explicit Konvertierungsfunktionen oder Konstruktoren verwenden?

Konvertierungskonstruktoren und nicht explizite Konvertierungsfunktionen können Mehrdeutigkeiten verursachen.

Man betrachte eine Struktur V , konvertierbar in int , eine aus U implizit konstruierbare Struktur U und eine für U und bool überladene Funktion f .

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 V .

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

Der Compiler weiß nicht, ob er den Konstruktor von U oder die Konvertierungsfunktion verwenden soll, um das V Objekt in einen Typ zu konvertieren, der an f .

Wenn entweder der Konstruktor von U oder die Konvertierungsfunktion von V explicit , würde es keine Mehrdeutigkeit geben, da nur die nicht explizite Konvertierung berücksichtigt würde. Wenn beide explizit sind, müsste der Aufruf von f mit einem Objekt vom Typ V mit einer expliziten Konvertierungs- oder Umwandlungsoperation erfolgen.

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, könnte die Funktion folgendermaßen aufgerufen werden:

print_intvector(3);

Was würde man von einem solchen Anruf erwarten? Eine Zeile mit 3 oder 3 Zeilen mit 0 ? (Wo der 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 handelt.

Wie Bjarne Stroustrup (in "Die Programmiersprache C ++", 4. Aufl., 35.2.1, S. 1011) über die Frage formuliert, warum std::duration nicht implizit aus einer einfachen Zahl konstruiert werden kann:

Wenn Sie wissen, was Sie meinen, machen Sie das explizit.


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

Das Zeichen 'x' wird implizit in int konvertiert und der String(int) -Konstruktor wird aufgerufen. Dies ist jedoch nicht das, was der Benutzer beabsichtigt hat. 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
};

Cpp Reference ist immer hilfreich !!! Details zum expliziten Spezifizierer finden Sie here . Möglicherweise müssen Sie auch implizite Konvertierungen und copy-initialization .

Schneller Blick

Der explizite Bezeichner gibt an, dass ein Konstruktor oder eine Konvertierungsfunktion (seit C ++ 11) keine impliziten Konvertierungen oder keine Kopierinitialisierung zulässt.

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
}

Das explicit -keyword kann verwendet werden, um den explicit Aufruf eines Konstruktors zu erzwingen.

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 Typumwandlungsoperatoren 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 erzwingt explicit -keyword nur explicit Umsetzungen, um gültig zu sein, sodass bool b = c; wäre in diesem Fall eine ungültige Besetzung. In Situationen wie diesen kann das explicit -keyword Programmierern dabei helfen, implizite, unbeabsichtigte Casts zu vermeiden. Diese Verwendung wurde in C++11 standardisiert.


Der Compiler darf eine implizite Konvertierung durchführen, um die Parameter in eine Funktion aufzulösen. Dies bedeutet, dass der Compiler Konstruktoren verwenden kann, die mit einem einzigen Parameter aufgerufen werden können , um von einem Typ in einen anderen zu konvertieren, 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 übernimmt:

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

und hier wird die DoBar Funktion aufgerufen.

int main ()
{
  DoBar (42);
}

Das Argument ist kein Foo Objekt, sondern ein int . Es gibt jedoch einen Konstruktor für Foo , der ein int sodass dieser Konstruktor verwendet werden kann, um den Parameter in den richtigen Typ zu konvertieren.

Der Compiler darf dies einmal für jeden Parameter.

Wenn Sie dem Konstruktor das explicit Schlüsselwort voranstellen, kann der Compiler diesen Konstruktor nicht für implizite Konvertierungen verwenden. Wenn Sie es zur obigen Klasse DoBar (42) wird beim Funktionsaufruf DoBar (42) ein Compilerfehler DoBar (42) . Es ist jetzt notwendig, die Konvertierung explizit mit DoBar (Foo (42))

Möglicherweise möchten Sie dies tun, um versehentliche Konstruktionen zu vermeiden, die Fehler verbergen können. Gedachtes Beispiel:

  • Sie haben eine MyString(int size) Klasse MyString(int size) mit einem Konstruktor, der eine Zeichenfolge der angegebenen Größe erstellt. Sie haben eine Funktion print(const MyString&) und rufen print(3) (wenn Sie eigentlich print("3") aufrufen wollten). Sie erwarten, dass es "3" druckt, aber es gibt stattdessen eine leere Zeichenfolge der Länge 3 aus.

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

Außerdem ist es immer eine gute Kodierungspraxis, wie Sie bereits einen Argumentkonstruktor (einschließlich solcher mit Standardwerten für arg2, arg3, ...) erstellt haben. Wie immer bei C ++: Wenn Sie dies nicht tun, wünschen Sie das auch ...

Eine weitere bewährte Methode für Klassen besteht darin, die Erstellung und Zuweisung von Kopien als privat zu definieren (dh deaktivieren Sie sie), sofern Sie sie nicht wirklich implementieren müssen. Dadurch wird vermieden, dass bei Verwendung der Methoden, die C ++ standardmäßig für Sie erstellt, möglicherweise Zeigerkopien verwendet werden. Ein anderer Weg, dies zu tun, ist von boost :: noncopyable.


Konstruktoren hängen eine implizite Konvertierung an. Um diese implizite Konvertierung zu unterdrücken, muss ein Konstruktor mit einem expliziten Parameter deklariert werden.

In C ++ 11 können Sie auch einen "operator type ()" mit dem Schlüsselwort here angeben. Mit dieser Angabe können Sie operator für explizite Konvertierungen verwenden, und direkte Initialisierung des Objekts.

PS Bei Verwendung der von USER definierten Transformationen (über Konstruktoren und Typkonvertierungsoperator) ist nur eine Ebene der impliziten Konvertierungen zulässig. Sie können diese Konvertierungen jedoch mit anderen Sprachkonvertierungen kombinieren

  • ganzzahlige Ränge (char bis int, float bis double);
  • Standart-Konvertierungen (int in double);
  • Zeiger von Objekten in Basisklasse und in Void * umwandeln;

Explizite Konvertierungskonstruktoren (nur C ++)

Der explizite Funktionsbezeichner steuert unerwünschte implizite Typkonvertierungen. Es kann nur in Deklarationen von Konstruktoren innerhalb einer Klassendeklaration verwendet werden. Mit Ausnahme des Standardkonstruktors sind die Konstruktoren in der folgenden Klasse beispielsweise Konvertierungskonstruktoren.

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

Die folgenden Erklärungen sind legal:

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

Die erste Deklaration ist äquivalent zu A c = A( 1 ); .

Wenn Sie den Konstruktor der Klasse als explicit deklarieren, wären die vorherigen Deklarationen ungültig.

Wenn Sie beispielsweise die Klasse als deklarieren:

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

Sie können nur Werte zuweisen, die den Werten des Klassentyps entsprechen.

Zum Beispiel sind die folgenden Aussagen 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);




explicit-constructor