Direkte Initialisierung
Initialisiert ein Objekt aus einem explizit gesetzten Satz von Konstruktorargumenten.
Inhalt |
[bearbeiten] Syntax
T object ( arg );T object |
(1) | ||||||||
T object { arg }; |
(2) | (seit C++11) | |||||||
T ( other )T |
(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:
Die Auswirkungen der direkten Initialisierung sind:
- Wenn
Tein Array-Typ ist,
|
(bis C++20) |
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
Tein Klassentyp ist,
|
(seit C++17) |
- werden die Konstruktoren von
Tuntersucht und der beste Treffer durch Überladungsauflösung ausgewählt. Der Konstruktor wird dann aufgerufen, um das Objekt zu initialisieren.
- werden die Konstruktoren von
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
Tein 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
Tbool 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
Tzu 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