web-dev-qa-db-de.com

Was ist ein Konvertierungskonstruktor in C++? Wofür ist das?

Ich habe gehört, dass C++ etwas namens "Konvertierungskonstruktoren" oder "Konvertierungskonstruktoren" hat. Was sind das und wofür sind sie? Ich sah es in Bezug auf diesen Code erwähnt:

class MyClass
{
  public:
     int a, b;
     MyClass( int i ) {}
}

 int main()
{
    MyClass M = 1 ;
}
38
octoback

Die Definition für einen Konvertierungskonstruktor unterscheidet sich zwischen C++ 03 und C++ 11. In beiden Fällen muss es ein nonexplicit -Konstruktor sein (andernfalls wäre es nicht an impliziten Konvertierungen beteiligt), aber für C++ 03 muss es auch mit einem einzelnen Argument aufrufbar sein. Das ist:

struct foo
{
  foo(int x);              // 1
  foo(char* s, int x = 0); // 2
  foo(float f, int x);     // 3
  explicit foo(char x);    // 4
};

Die Konstruktoren 1 und 2 konvertieren beide Konstruktoren in C++ 03 und C++ 11. Konstruktor 3, der zwei Argumente annehmen muss, ist in C++ 11 nur ein Konvertierungskonstruktor. Der letzte, Konstruktor 4, ist kein Konvertierungskonstruktor, da er explicit ist.

  • C++ : §12.3.1

    Ein Konstruktor, der ohne den Funktionsbezeichner explicit deklariert wurde und mit einem einzelnen Parameter aufgerufen werden kann, gibt eine Konvertierung vom Typ seines ersten Parameters in an die Art seiner Klasse. Ein solcher Konstruktor wird Konvertierungskonstruktor genannt.

  • C++ 11: §12.3.1

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

Warum werden Konstruktoren mit mehr als einem Parameter als Konvertierungskonstruktoren in C++ 11 betrachtet? Dies liegt daran, dass der neue Standard eine praktische Syntax für die Übergabe von Argumenten und die Rückgabe von Werten mithilfe von Klammer-Init-Listen bereitstellt. Betrachten Sie das folgende Beispiel:

foo bar(foo f)
{
  return {1.0f, 5};
}

Die Möglichkeit, den Rückgabewert als Klammer-Init-Liste anzugeben, wird als Konvertierung angesehen. Dies verwendet den Konvertierungskonstruktor für foo, der ein float und ein int annimmt. Zusätzlich können wir diese Funktion aufrufen, indem wir bar({2.5f, 10}) ausführen. Dies ist auch eine Konvertierung. Da es sich um Konvertierungen handelt, ist es sinnvoll, dass die verwendeten Konstruktoren Konstruktoren konvertieren .

Es ist daher wichtig zu beachten, dass das Erstellen des Konstruktors von foo, der ein float und ein int mit dem Funktionsspezifizierer explicit annimmt, das Kompilieren des obigen Codes stoppen würde. Die obige neue Syntax kann nur verwendet werden, wenn ein Konvertierungskonstruktor für die Ausführung der Aufgabe verfügbar ist.

  • C++ 11: §6.6.3:

    Eine return -Anweisung mit einer Klammer-Init-Liste initialisiert das Objekt oder die Referenz, die von der Funktion durch Kopierlisten-Initialisierung zurückgegeben werden soll ( 8.5.4) aus der angegebenen Initialisierungsliste.

    §8.5:

    Die Initialisierung, die beim [...] Übergeben von Argumenten auftritt, wird als Kopierinitialisierung [...] bezeichnet.

    §12.3.1:

    Ein expliziter Konstruktor erstellt Objekte genau wie nicht-explizite Konstruktoren, jedoch nur, wenn die Syntax für die direkte Initialisierung (8.5) oder Casts (5.2.9, 5.4) explizit verwendet werden.

53

implizit mit konvertierendem Konstruktor konvertieren

Lassen Sie uns das Beispiel in der Frage komplexer machen

class MyClass
{
  public:
     int a, b;
     MyClass( int i ) {}
     MyClass( const char* n, int k = 0 ) {}
     MyClass( MyClass& obj ) {}
}

Zuerst konvertieren zwei Konstruktoren Konstruktoren. Der dritte ist ein Kopierkonstruktor und als solcher ein weiterer Konvertierungskonstruktor.

Ein Konvertierungskonstruktor ermöglicht die implizite Konvertierung vom Argumenttyp in den Konstruktortyp. Hier ermöglicht der erste Konstruktor die Konvertierung von einer int in ein Objekt der Klasse MyClass. Der zweite Konstruktor ermöglicht die Konvertierung von einem String in ein Objekt der Klasse MyClass. Und drittens ... von einem Objekt der Klasse MyClass zu einem Objekt der Klasse MyClass!

Um ein konvertierender Konstruktor zu sein, muss der Konstruktor ein einzelnes Argument haben (im zweiten Argument hat das zweite Argument einen Standardwert) und muss ohne das Schlüsselwort explicit deklariert werden.

Dann kann die Initialisierung in main folgendermaßen aussehen:

int main()
{
    MyClass M = 1 ;
    // which is an alternative to
    MyClass M = MyClass(1) ;

    MyClass M = "super" ;
    // which is an alternative to
    MyClass M = MyClass("super", 0) ;
    // or
    MyClass M = MyClass("super") ;
}

Explizites Schlüsselwort und Konstruktoren

Was wäre, wenn wir das Schlüsselwort explicit verwendet hätten?

class MyClass
{
  public:
     int a, b;
     explicit MyClass( int i ) {}
}

Dann würde der Compiler nicht akzeptieren

   int main()
    {
        MyClass M = 1 ;
    }

da dies eine implizite Konvertierung ist. Stattdessen muss ich schreiben

   int main()
    {
        MyClass M(1) ;
        MyClass M = MyClass(1) ;
        MyClass* M = new MyClass(1) ;
        MyClass M = (MyClass)1;
        MyClass M = static_cast<MyClass>(1);
    }

Das explicit-Schlüsselwort ist immer zu verwenden, um die implizite Konvertierung für einen Konstruktor zu verhindern, und es gilt für den Konstruktor in einer Klassendeklaration.

13
octoback

Ein Konvertierungskonstruktor ist ein Einzelparameter-Konstruktor, der ohne expliziten Funktionsspezifizierer deklariert wird. Der Compiler konvertiert Objekte vom Typ des ersten Parameters in Konvertierungskonstruktoren in den Typ der Klasse des Konvertierungskonstruktors.

1
Fazail awan