Namensräume
Varianten
Aktionen

Kopierinitialisierung

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

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;

catch (T objekt)

(5)
T array[N] = {andere_sequenz}; (6)

[bearbeiten] Erklärung

Kopierinitialisierung wird in folgenden Situationen durchgeführt:

1) Wenn eine benannte Variable (automatisch, statisch oder Thread-lokal) vom Nicht-Referenztyp T mit einem Initialisierer deklariert wird, der aus einem Gleichheitszeichen gefolgt von einem Ausdruck besteht.
2) (bis C++11) Wenn eine benannte Variable vom Skalarmtyp 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).
3) Beim Übergeben eines Arguments an eine Funktion per Wert.
4) Beim Zurückgeben aus einer Funktion, die per Wert zurückgibt.
5) Beim Werfen oder Auffangen einer Ausnahme per Wert.
6) Als Teil der Aggregatinitialisierung, um jedes Element zu initialisieren, für das ein Initialisierer bereitgestellt wird.

Die Auswirkungen der Kopierinitialisierung sind:

  • Zuerst, wenn T ein Klassentyp ist und der Initialisierer ein prvalue-Ausdruck ist, dessen cv-unqualified Typ derselbe Klassentyp wie T ist, wird der Initialisiererausdruck selbst anstelle eines daraus erzeugten Temporärspeichers zur Initialisierung des Zielobjekts verwendet: siehe Kopier-Eliminierung.
(seit C++17)
  • Andernfalls, wenn T ein Klassentyp ist und die cv-unqualified Version des Typs von anderes 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 aufgerufen, um das Objekt zu initialisieren.
  • Andernfalls, wenn T ein Klassentyp ist und die cv-unqualified Version des Typs von anderes nicht T oder von T abgeleitet ist, oder wenn T ein Nicht-Klassentyp ist, aber der Typ von anderes ein Klassentyp ist, werden benutzerdefinierte Konvertierungssequenzen untersucht, die von Typ von anderes nach T (oder nach einem von T abgeleiteten Typ, wenn 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, 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 von T, 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 T noch der Typ von anderes Klassentypen sind), werden, falls erforderlich, Standardkonvertierungen verwendet, um den Wert von anderes in die cv-unqualified Version von T zu 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

[bearbeiten] Siehe auch