Namespaces
Variants

Copy-initialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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 ;

catch ( T Objekt )

(5)
T Array [ N ] = { other-sequence }; (6)

Erklärung

Copy-Initialisierung wird in den folgenden Situationen durchgeführt:

1) Wenn eine benannte Variable (automatisch, statisch oder thread-lokal) eines Nicht-Referenztyps T mit einem Initialisierer deklariert wird, der aus einem Gleichheitszeichen gefolgt von einem Ausdruck besteht.
2) (bis C++11) Wenn eine benannte Variable eines skalaren Typs 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).
3) Wenn ein Argument an eine Funktion als Wert übergeben wird.
4) Beim Zurückkehren aus einer Funktion, die by value zurückgibt.
5) Beim Werfen oder Fangen einer Ausnahme als Wert.
6) Als Teil der aggregate initialization , um jedes Element zu initialisieren, für das ein Initialisierer bereitgestellt wird.

Die Auswirkungen der Copy-Initialisierung sind:

  • Zuerst, wenn T ein Klassentyp ist und der Initialisierer ein Prvalue -Ausdruck ist, dessen cv-unqualifizierter Typ derselbe Klasse wie T entspricht, wird der Initialisiererausdruck selbst verwendet, um das Zielobjekt zu initialisieren, anstatt eines daraus materialisierten Temporärs: siehe Copy Elision .
(seit C++17)
  • Zuerst (bis C++17) Andernfalls (seit C++17) , falls T ein Klassentyp ist und die cv-unqualifizierte Version des Typs von other T oder eine von T abgeleitete Klasse ist, werden die nicht-expliziten Konstruktoren von T untersucht und die beste Übereinstimmung durch Überladungsauflösung ausgewählt. Dieser Konstruktor wird dann zur Initialisierung des Objekts aufgerufen.
  • Andernfalls, falls T ein Klassentyp ist und die cv-unqualifizierte Version des Typs von other weder T noch von T abgeleitet ist, oder falls T ein Nicht-Klassentyp ist, aber der Typ von other ein Klassentyp ist, werden benutzerdefinierte Konvertierungssequenzen untersucht, die vom Typ von other zu T (oder zu einem von T abgeleiteten Typ, falls T ein 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 von T ist, 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 T noch der Typ von other Klassentypen sind), werden Standardkonvertierungen verwendet, falls notwendig, um den Wert von other in die cv-unqualifizierte Version von T zu 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

Siehe auch