Namensräume
Varianten
Aktionen

Direkte Initialisierung

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 explizit gesetzten Satz von Konstruktorargumenten.

Inhalt

[bearbeiten] Syntax

T object ( arg );

T object ( arg1, arg2, ... );

(1)
T object { arg }; (2) (seit C++11)
T ( other )

T ( arg1, arg2, ... )

(3)
static_cast< T >( other ) (4)
new T( args, ... ) (5)
Class::Class() : member( args, ... ) { ... } (6)
[arg]() { ... } (7) (seit C++11)

[bearbeiten] Erklärung

Direkte Initialisierung erfolgt in folgenden Situationen:

1) Initialisierung mit einer nicht-leeren geklammerten Liste von Ausdrücken oder braced-init-Listen(seit C++11).
2) Initialisierung eines Objekts eines Nicht-Klassentyps mit einem einzelnen in geschweifte Klammern eingeschlossenen Initialisierer (Hinweis: Für Klassentypen und andere Verwendungen von braced-init-Listen siehe Listeninitialisierung)(seit C++11).
3) Initialisierung eines prvalue-Temporärs(bis C++17)des Ergebnisobjekts eines prvalue(seit C++17) durch einen funktionsartigen Cast oder mit einer geklammerten Ausdrücksliste.
4) Initialisierung eines prvalue-Temporärs(bis C++17)des Ergebnisobjekts eines prvalue(seit C++17) durch einen static_cast-Ausdruck.
5) Initialisierung eines Objekts mit dynamischer Speicherbelegung durch einen new-Ausdruck mit einem Initialisierer.
6) Initialisierung einer Basisklasse oder eines nicht-statischen Mitglieds durch eine Konstruktor-Initialisiererliste.
7) Initialisierung von Mitgliedern eines Closure-Objekts aus den Variablen, die per Kopie in einem Lambda-Ausdruck erfasst wurden.

Die Auswirkungen der direkten Initialisierung sind:

  • Wenn T ein Array-Typ ist,
  • ist das Programm ill-formed.
(bis C++20)
  • wird das Array wie bei der Aggregatinitialisierung initialisiert, mit der Ausnahme, dass Konvertierungen mit Verengung (narrowing conversions) erlaubt sind und alle Elemente ohne Initialisierer wertinitialisiert werden.
struct A
{
    explicit A(int i = 0) {}
};
 
A a[2](A(1)); // OK: initializes a[0] with A(1) and a[1] with A()
A b[2]{A(1)}; // error: implicit copy-list-initialization of b[1]
              //        from {} selected explicit constructor
(seit C++20)
  • Wenn T ein Klassentyp ist,
  • wird, wenn der Initialisierer ein prvalue-Ausdruck ist, dessen Typ dieselbe Klasse wie T ist (unter Ignorierung von cv-Qualifikationen), der Initialisiererausdruck selbst und nicht ein daraus materialisiertes Temporär-Objekt zur Initialisierung des Zielobjekts verwendet.
    (Vor C++17 kann der Compiler die Konstruktion aus dem prvalue-Temporär-Objekt in diesem Fall weglassen, aber der entsprechende Konstruktor muss immer noch zugänglich sein: siehe Kopier-Elision)
(seit C++17)
  • werden die Konstruktoren von T untersucht und der beste Treffer durch Überladungsauflösung ausgewählt. Der Konstruktor wird dann aufgerufen, um das Objekt zu initialisieren.
  • andernfalls, wenn der Zieltyp eine (möglicherweise cv-qualifizierte) Aggregatklasse ist, wird sie wie in der Aggregatinitialisierung beschrieben initialisiert, mit der Ausnahme, dass Konvertierungen mit Verengung erlaubt sind, benannte Initialisierer nicht zulässig sind, ein an eine Referenz gebundenes Temporär-Objekt seine Lebensdauer nicht verlängert, keine Klammer-Elision stattfindet und alle Elemente ohne Initialisierer wertinitialisiert werden.
struct B
{
    int a;
    int&& r;
};
 
int f();
int n = 10;
 
B b1{1, f()};            // OK, lifetime is extended
B b2(1, f());            // well-formed, but dangling reference
B b3{1.0, 1};            // error: narrowing conversion
B b4(1.0, 1);            // well-formed, but dangling reference
B b5(1.0, std::move(n)); // OK
(seit C++20)
  • Andernfalls, wenn T ein Nicht-Klassentyp ist, aber der Quelltyp ein Klassentyp ist, werden die Konvertierungsfunktionen des Quelltyps und seiner Basisklassen, falls vorhanden, untersucht und der beste Treffer durch Überladungsauflösung ausgewählt. Die ausgewählte benutzerdefinierte Konvertierung wird dann verwendet, um den Initialisiererausdruck in das zu initialisierende Objekt zu konvertieren.
  • Andernfalls, wenn T bool ist und der Quelltyp std::nullptr_t ist, ist der Wert des initialisierten Objekts false.
  • Andernfalls werden Standardkonvertierungen verwendet, um gegebenenfalls den Wert von other in die cv-unqualifizierte Version von T zu konvertieren, und der Anfangswert des zu initialisierenden Objekts ist der (möglicherweise konvertierte) Wert.

[bearbeiten] Hinweise

Die direkte Initialisierung ist permissiver als die Kopierinitialisierung: Die Kopierinitialisierung berücksichtigt nur nicht-explizite Konstruktoren und nicht-explizite benutzerdefinierte Konvertierungsfunktionen, während die direkte Initialisierung alle Konstruktoren und alle benutzerdefinierten Konvertierungsfunktionen berücksichtigt.

Im Falle von Mehrdeutigkeit zwischen einer Variablendeklaration unter Verwendung der direkten Initialisierungssyntax (1) (mit runden Klammern) und einer Funktionsdeklaration wählt der Compiler immer die Funktionsdeklaration. Diese Regel zur Auflösung von Mehrdeutigkeiten ist manchmal kontraintuitiv und wird als most vexing parse bezeichnet.

#include <fstream>
#include <iterator>
#include <string>
 
int main()
{
    std::ifstream file("data.txt");
 
    // The following is a function declaration:
    std::string foo1(std::istreambuf_iterator<char>(file),
                     std::istreambuf_iterator<char>());
    // It declares a function called foo1, whose return type is std::string,
    // first parameter has type std::istreambuf_iterator<char> and the name "file",
    // second parameter has no name and has type std::istreambuf_iterator<char>(),
    // which is rewritten to function pointer type std::istreambuf_iterator<char>(*)()
 
    // Pre-C++11 fix (to declare a variable) - add extra parentheses around one
    // of the arguments:
    std::string str1((std::istreambuf_iterator<char>(file)),
                      std::istreambuf_iterator<char>());
 
    // Post-C++11 fix (to declare a variable) - use list-initialization for any
    // of the arguments:
    std::string str2(std::istreambuf_iterator<char>{file}, {});
}

[bearbeiten] Beispiel

#include <iostream>
#include <memory>
#include <string>
 
struct Foo
{
    int mem;
    explicit Foo(int n) : mem(n) {}
};
 
int main()
{
    std::string s1("test"); // constructor from const char*
    std::string s2(10, 'a');
 
    std::unique_ptr<int> p(new int(1));  // OK: explicit constructors allowed
//  std::unique_ptr<int> p = new int(1); // error: constructor is explicit
 
    Foo f(2); // f is direct-initialized:
              // constructor parameter n is copy-initialized from the rvalue 2
              // f.mem is direct-initialized from the parameter n
//  Foo f2 = 2; // error: constructor is explicit
 
    std::cout << s1 << ' ' << s2 << ' ' << *p << ' ' << f.mem  << '\n';
}

Ausgabe

test aaaaaaaaaa 1 2

[bearbeiten] Siehe auch