Copy-initialization
Initialisiert ein Objekt von einem anderen Objekt.
Inhaltsverzeichnis |
Syntax
T
Objekt
=
other
;
|
(1) | ||||||||
T
Objekt
=
{
other
};
|
(2) | (bis C++11) | |||||||
f
(
other
)
|
(3) | ||||||||
return
other
;
|
(4) | ||||||||
throw
Objekt
;
|
(5) | ||||||||
T
Array
[
N
] = {
other-sequence
};
|
(6) | ||||||||
Erklärung
Copy-Initialisierung wird in den 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 in geschweiften Klammern eingeschlossenen Ausdruck besteht (Hinweis: Ab C++11 wird dies als
Listeninitialisierung
klassifiziert, und einschränkende Konvertierung ist nicht erlaubt).
Die Auswirkungen der Copy-Initialisierung sind:
|
(seit C++17) |
-
Zuerst
(bis C++17)
Andernfalls
(seit C++17)
, falls
Tein Klassentyp ist und die cv-unqualifizierte Version des Typs von otherToder eine vonTabgeleitete Klasse ist, werden die nicht-expliziten Konstruktoren vonTuntersucht und die beste Übereinstimmung durch Überladungsauflösung ausgewählt. Dieser Konstruktor wird dann zur Initialisierung des Objekts aufgerufen.
-
Andernfalls, falls
Tein Klassentyp ist und die cv-unqualifizierte Version des Typs von other wederTnoch vonTabgeleitet ist, oder fallsTein Nicht-Klassentyp ist, aber der Typ von other ein Klassentyp ist, werden benutzerdefinierte Konvertierungssequenzen untersucht, die vom Typ von other zuT(oder zu einem vonTabgeleiteten Typ, fallsTein Klassentyp ist und eine Konvertierungsfunktion verfügbar ist) konvertieren können, und die beste wird durch Überladungsauflösung ausgewählt. Das Ergebnis der Konvertierung, das ein Rvalue-Temporärobjekt (bis C++11) Prvalue-Temporärobjekt (seit C++11) (bis C++17) Prvalue-Ausdruck (seit C++17) der cv-unqualifizierten Version vonTist, falls ein konvertierender Konstruktor verwendet wurde, wird dann verwendet, um das Objekt direkt zu initialisieren . Der letzte Schritt wird normalerweise optimiert und das Ergebnis der Konvertierung wird direkt im für das Zielobjekt reservierten 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 other Klassentypen sind), werden Standardkonvertierungen verwendet, falls notwendig, um den Wert von other in die cv-unqualifizierte Version vonTzu konvertieren.
Hinweise
Copy-Initialisierung ist weniger zulässig als Direktinitialisierung: explicit constructors sind keine converting constructors und werden für die Copy-Initialisierung nicht berücksichtigt.
struct Exp { explicit Exp(const char*) {} }; // nicht konvertierbar von const char* Exp e1("abc"); // OK Exp e2 = "abc"; // Fehler, Copy-Initialisierung berücksichtigt keinen explicit-Konstruktor struct Imp { Imp(const char*) {} }; // konvertierbar von const char* Imp i1("abc"); // OK Imp i2 = "abc"; // OK
Zusätzlich muss die implizite Konvertierung bei der Kopierinitialisierung direkt
T
aus dem Initialisierer erzeugen, während z.B. die Direktinitialisierung eine implizite Konvertierung vom Initialisierer zu einem Argument des Konstruktors von
T
erwartet.
struct S { S(std::string) {} }; // implizit konvertierbar von std::string S s("abc"); // OK: Konvertierung von const char[4] zu std::string S s = "abc"; // Fehler: keine Konvertierung von const char[4] zu S S s = "abc"s; // OK: Konvertierung von std::string zu S
Wenn other ein Rvalue-Ausdruck ist, wird ein Move Constructor durch Überladungsauflösung ausgewählt und während der Copy-Initialisierung aufgerufen. Dies wird weiterhin als Copy-Initialisierung betrachtet; es gibt keinen speziellen Begriff (z.B. Move-Initialisierung) für diesen Fall.
Implizite Konvertierung
wird im Sinne der Kopierinitialisierung definiert: Wenn ein Objekt vom Typ
T
mit dem Ausdruck
E
kopierinitialisiert werden kann, dann ist
E
implizit konvertierbar zu
T
.
Das Gleichheitszeichen,
=
, in der Copy-Initialisierung einer benannten Variable steht nicht in Beziehung zum Zuweisungsoperator. Zuweisungsoperatorüberladungen haben keine Auswirkung auf die Copy-Initialisierung.
Beispiel
#include <memory> #include <string> #include <utility> struct A { operator int() { return 12;} }; struct B { B(int) {} }; int main() { std::string s = "test"; // OK: Konstruktor ist nicht explizit std::string s2 = std::move(s); // diese Kopierinitialisierung führt einen Move durch // std::unique_ptr<int> p = new int(1); // Fehler: Konstruktor ist explizit std::unique_ptr<int> p(new int(1)); // OK: Direktinitialisierung int n = 3.14; // Gleitkomma-Ganzzahl-Konvertierung const int b = n; // const spielt keine Rolle int c = b; // ...auf beide Weisen A a; B b0 = 12; // B b1 = a; // < Fehler: Konvertierung von 'A' zu nicht-skalarem Typ 'B' angefordert B b2{a}; // < identisch, ruft A::operator int(), dann B::B(int) auf B b3 = {a}; // < auto b4 = B{a}; // < // b0 = a; // < Fehler, Zuweisungsoperatorüberladung benötigt [](...){}(c, b0, b3, b4); // simulieren, dass diese Variablen verwendet werden }
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 5 | C++98 |
die CV-Qualifikation des Zieltyps wird auf
das durch einen konvertierenden Konstruktor initialisierte Temporär angewendet |
das Temporär ist nicht CV-qualifiziert |
| CWG 177 | C++98 |
die Wertkategorie des während der
Copy-Initialisierung eines Klassenobjekts erstellten Temporärs ist nicht spezifiziert |
als Rvalue spezifiziert |