Namespaces
Variants

Direct-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 aus einem expliziten Satz von Konstruktorargumenten.

Inhaltsverzeichnis

Syntax

T Objekt ( arg );

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

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

T ( arg1, arg2, ... )

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

Erklärung

Direkte Initialisierung wird in den folgenden Situationen durchgeführt:

1) Initialisierung mit einer nicht-leeren parenthesisierten Liste von Ausdrücken oder braced-init-lists (since C++11) .
2) Initialisierung eines Objekts von Nicht-Klassen-Typ mit einem einzelnen geschweiften Klammern-Initialisierer (Hinweis: für Klassentypen und andere Verwendungen von Braced-Init-List, siehe list-initialization ) (since C++11) .
3) Initialisierung von einem prvalue-Temporär (bis C++17) dem Ergebnisobjekt eines prvalue (seit C++17) durch Function-Style Cast oder mit einer eingeklammerten Ausdrucksliste.
4) Initialisierung von einem prvalue-Temporär (bis C++17) des Ergebnisobjekts eines prvalue (seit C++17) durch einen static_cast -Ausdruck.
5) Initialisierung eines Objekts mit dynamischer Speicherdauer durch einen new-Ausdruck mit einem Initialisierer.
6) Initialisierung einer Basisklasse oder eines nicht-statischen Members durch den Konstruktor- initializer list .
7) Initialisierung von Closure-Objektmitgliedern aus den durch Kopie erfassten Variablen in einem Lambda-Ausdruck.

Die Auswirkungen der Direktinitialisierung sind:

  • Wenn T ein Array-Typ ist,
  • das Programm ist fehlerhaft.
(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 T ein Klassentyp ist,
  • wenn der Initialisierer ein Prvalue -Ausdruck ist, dessen Typ derselbe ist wie der von T (unter Ignorierung von CV-Qualifikationen), wird der Initialisierer-Ausdruck selbst verwendet, um das Zielobjekt zu initialisieren, anstatt eines daraus materialisierten Temporärs.
    (Vor C++17 konnte der Compiler in diesem Fall die Konstruktion aus dem Prvalue-Temporär eliminieren, aber der entsprechende Konstruktor musste dennoch zugänglich sein: siehe Copy Elision )
(seit C++17)
  • die Konstruktoren von T werden untersucht und die beste Übereinstimmung wird 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 unter aggregate initialization beschrieben initialisiert, außer dass einschränkende Konvertierungen erlaubt sind, designierte Initialisierer nicht zulässig sind, ein an eine Referenz gebundenes temporäres Objekt keine Lebensdauerverlängerung erfährt, keine Klammerelision stattfindet und alle Elemente ohne Initialisierer value-initialized 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, falls T ein Nicht-Klassentyp ist, aber der Quelltyp ein Klassentyp ist, werden die Konvertierungsfunktionen des Quelltyps und seiner Basisklassen, falls vorhanden, geprüft und die beste Übereinstimmung durch Überladungsauflösung ausgewählt. Die ausgewählte benutzerdefinierte Konvertierung wird dann verwendet, um den Initialisierungsausdruck in das zu initialisierende Objekt umzuwandeln.
  • Andernfalls, falls T bool ist und der Quelltyp std::nullptr_t ist, ist der Wert des initialisierten Objekts false .
  • Andernfalls werden Standardkonvertierungen verwendet, falls notwendig, um den Wert von other in die cv-unqualifizierte Version von T umzuwandeln, und der Anfangswert des zu initialisierenden Objekts ist der (möglicherweise konvertierte) Wert.

Hinweise

Direct-Initialisierung ist restriktiver als Copy-Initialisierung: Copy-Initialisierung berücksichtigt nur nicht- explicit Konstruktoren und nicht-explizite benutzerdefinierte conversion functions , während Direct-Initialisierung alle Konstruktoren und alle benutzerdefinierten Konversionfunktionen berücksichtigt.

Im Falle von Mehrdeutigkeit zwischen einer Variablendeklaration mit der Direktinitialisierungssyntax (1) (mit runden Klammern) und einer Funktionsdeklaration , wählt der Compiler stets die Funktionsdeklaration. Diese Disambiguierungsregel ist manchmal kontraintuitiv und wurde als most vexing parse bezeichnet.

#include <fstream>
#include <iterator>
#include <string>
int main()
{
    std::ifstream file("data.txt");
    // Das Folgende ist eine Funktionsdeklaration:
    std::string foo1(std::istreambuf_iterator<char>(file),
                     std::istreambuf_iterator<char>());
    // Sie deklariert eine Funktion namens foo1, deren Rückgabetyp std::string ist,
    // der erste Parameter hat den Typ std::istreambuf_iterator<char> und den Namen "file",
    // der zweite Parameter hat keinen Namen und den Typ std::istreambuf_iterator<char>(),
    // was in den Funktionszeigertyp std::istreambuf_iterator<char>(*)() umgeschrieben wird
    // Pre-C++11 Korrektur (um eine Variable zu deklarieren) - zusätzliche Klammern um einen
    // der Parameter setzen:
    std::string str1((std::istreambuf_iterator<char>(file)),
                      std::istreambuf_iterator<char>());
    // Post-C++11 Korrektur (um eine Variable zu deklarieren) - Listeninitialisierung für einen
    // der Parameter verwenden:
    std::string str2(std::istreambuf_iterator<char>{file}, {});
}

Beispiel

#include <iostream>
#include <memory>
#include <string>
struct Foo
{
    int mem;
    explicit Foo(int n) : mem(n) {}
};
int main()
{
    std::string s1("test"); // Konstruktor von const char*
    std::string s2(10, 'a');
    std::unique_ptr<int> p(new int(1));  // OK: explizite Konstruktoren erlaubt
//  std::unique_ptr<int> p = new int(1); // Fehler: Konstruktor ist explizit
    Foo f(2); // f ist direkt initialisiert:
              // Konstruktorparameter n ist kopierinitialisiert vom Rvalue 2
              // f.mem ist direkt initialisiert vom Parameter n
//  Foo f2 = 2; // Fehler: Konstruktor ist explizit
    std::cout << s1 << ' ' << s2 << ' ' << *p << ' ' << f.mem  << '\n';
}

Ausgabe:

test aaaaaaaaaa 1 2

Siehe auch