Kopierinitialisierung
Initialisiert ein Objekt aus einem anderen Objekt.
Inhalt |
[bearbeiten] Syntax
T objekt = anderes; |
(1) | ||||||||
T objekt = {anderes}; |
(2) | (bis C++11) | |||||||
f(anderes) |
(3) | ||||||||
return anderes; |
(4) | ||||||||
throw objekt;
|
(5) | ||||||||
T array[N] = {andere_sequenz}; |
(6) | ||||||||
[bearbeiten] Erklärung
Kopierinitialisierung wird in folgenden Situationen durchgeführt:
T mit einem Initialisierer deklariert wird, der aus einem Gleichheitszeichen gefolgt von einem Ausdruck besteht.T mit einem Initialisierer deklariert wird, der aus einem Gleichheitszeichen gefolgt von einem klammerumschlossenen Ausdruck besteht (Hinweis: ab C++11 wird dies als Listeninitialisierung klassifiziert, und eine Verengungskonvertierung ist nicht erlaubt).Die Auswirkungen der Kopierinitialisierung sind:
|
(seit C++17) |
- Andernfalls, wenn
Tein Klassentyp ist und die cv-unqualified Version des Typs von anderesToder eine vonTabgeleitete Klasse ist, werden die nicht-expliziten Konstruktoren vonTuntersucht und die beste Übereinstimmung durch Überladungsauflösung ausgewählt. Dieser Konstruktor wird dann aufgerufen, um das Objekt zu initialisieren.
- Andernfalls, wenn
Tein Klassentyp ist und die cv-unqualified Version des Typs von anderes nichtToder vonTabgeleitet ist, oder wennTein Nicht-Klassentyp ist, aber der Typ von anderes ein Klassentyp ist, werden benutzerdefinierte Konvertierungssequenzen untersucht, die von Typ von anderes nachT(oder nach einem vonTabgeleiteten Typ, wennTein Klassentyp ist und eine Konvertierungsfunktion verfügbar ist) konvertieren können, und die beste wird durch Überladungsauflösung ausgewählt. Das Ergebnis der Konvertierung, ein rvalue-Temporärspeicher(bis C++11)prvalue-Temporärspeicher(seit C++11)(bis C++17)prvalue-Ausdruck(seit C++17) des cv-unqualified Typs vonT, wenn ein Konstruktor mit Konvertierung verwendet wurde, wird dann verwendet, um das Objekt direkt zu initialisieren. Der letzte Schritt wird normalerweise optimiert und das Ergebnis der Konvertierung wird direkt in den für das Zielobjekt zugewiesenen Speicher konstruiert, aber der entsprechende Konstruktor (Move oder Copy) muss zugänglich sein, auch wenn er nicht verwendet wird.(bis C++17)
- Andernfalls (wenn weder
Tnoch der Typ von anderes Klassentypen sind), werden, falls erforderlich, Standardkonvertierungen verwendet, um den Wert von anderes in die cv-unqualified Version vonTzu konvertieren.
[bearbeiten] Anmerkungen
Kopierinitialisierung ist weniger permissiv als direkte Initialisierung: explizite Konstruktoren sind keine Konstruktor mit Konvertierung und werden für die Kopierinitialisierung nicht berücksichtigt.
struct Exp { explicit Exp(const char*) {} }; // not convertible from const char* Exp e1("abc"); // OK Exp e2 = "abc"; // Error, copy-initialization does not consider explicit constructor struct Imp { Imp(const char*) {} }; // convertible from const char* Imp i1("abc"); // OK Imp i2 = "abc"; // OK
Zusätzlich muss die implizite Konvertierung bei der Kopierinitialisierung T direkt aus dem Initialisierer erzeugen, während z.B. die direkte Initialisierung eine implizite Konvertierung vom Initialisierer zu einem Argument des Konstruktors von T erwartet.
struct S { S(std::string) {} }; // implicitly convertible from std::string S s("abc"); // OK: conversion from const char[4] to std::string S s = "abc"; // Error: no conversion from const char[4] to S S s = "abc"s; // OK: conversion from std::string to S
Wenn anderes ein rvalue-Ausdruck ist, wird ein Move-Konstruktor durch Überladungsauflösung ausgewählt und während der Kopierinitialisierung aufgerufen. Dies wird weiterhin als Kopierinitialisierung betrachtet; es gibt keinen speziellen Begriff (z.B. Move-Initialisierung) für diesen Fall.
Implizite Konvertierung ist definiert über Kopierinitialisierung: Wenn ein Objekt vom Typ T mit dem Ausdruck E kopierinitialisiert werden kann, dann ist E implizit nach T konvertierbar.
Das Gleichheitszeichen = bei der Kopierinitialisierung einer benannten Variablen hat keine Beziehung zum Zuweisungsoperator. Überladungen des Zuweisungsoperators haben keinen Einfluss auf die Kopierinitialisierung.
[bearbeiten] Beispiel
#include <memory> #include <string> #include <utility> struct A { operator int() { return 12;} }; struct B { B(int) {} }; int main() { std::string s = "test"; // OK: constructor is non-explicit std::string s2 = std::move(s); // this copy-initialization performs a move // std::unique_ptr<int> p = new int(1); // error: constructor is explicit std::unique_ptr<int> p(new int(1)); // OK: direct-initialization int n = 3.14; // floating-integral conversion const int b = n; // const doesn't matter int c = b; // ...either way A a; B b0 = 12; // B b1 = a; // < error: conversion from 'A' to non-scalar type 'B' requested B b2{a}; // < identical, calling A::operator int(), then B::B(int) B b3 = {a}; // < auto b4 = B{a}; // < // b0 = a; // < error, assignment operator overload needed [](...){}(c, b0, b3, b4); // pretend these variables are used }
[bearbeiten] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 5 | C++98 | Die CV-Qualifikation des Zieltyps wird angewendet auf das Temporärspeicherobjekt, das von einem Konvertierungskonstruktor initialisiert wird |
das Temporärspeicherobjekt ist nicht CV-qualifiziert |
| CWG 177 | C++98 | Die Wertkategorie des Temporärspeicherobjekts, das während der Kopierinitialisierung eines Klassenobjekts erstellt wird, ist nicht spezifiziert |
als rvalue spezifiziert |