W C++ wyróżnia się bezpośrednią (direct) i pośrednią (copy) inicjalizację obiektów:
Bezpośrednia inicjalizacja:
MyClass obj(arg1, arg2); int x(5);
Dla klas bezpośrednio wywołuje odpowiedni konstruktor.
Pośrednia inicjalizacja:
MyClass obj = MyClass(arg1, arg2); int x = 5;
Tworzy obiekt tymczasowy, a następnie go kopiuje (lub przenosi) (używa konstruktora kopiującego/przenoszącego).
Różnice:
Przykład:
struct NonCopyable { NonCopyable(int) {} NonCopyable(const NonCopyable&) = delete; }; NonCopyable a(5); // OK: bezpośrednie NonCopyable b = NonCopyable(5); // OK z optymalizacją, błąd przed C++17 bez niej NonCopyable c = 5; // Błąd: brak konstruktora kopiowania
"Czy pośrednia inicjalizacja może wywołać więcej wywołań konstruktorów niż bezpośrednia inicjalizacja, jeśli używa się C++11 i włączonego kompilatora optymalizującego?"
Odpowiedź: Teoretycznie — tak. Bez copy elision pośrednia inicjalizacja tworzy obiekt tymczasowy, a następnie wywołuje konstruktor kopiowania lub przenoszenia. Bezpośrednia inicjalizacja zawsze wywołuje tylko główny konstruktor. Niemniej jednak nowoczesne kompilatory z włączoną optymalizacją (C++17 i wyżej) eliminują tę różnicę dzięki guaranteed copy elision.
Historia
W projekcie finansowym użyto pośredniej inicjalizacji z obiektem tymczasowym, a wymagany typ nie miał konstruktora kopiującego. Aplikacja nie kompilowała się na starych kompilatorach (przed C++17), mimo że direct-init działał.
Historia
W narzędziu do serializacji danych programiści uważali, że direct-init i copy-init są równoważne. W rezultacie występowały zbędne kopie dużych struktur oraz problemy z wydajnością.
Historia
Podczas inicjalizacji globalnych tablic obiektów użytkowników użyto pośredniej inicjalizacji. Błędy pojawiały się tylko na jednej platformie, gdzie copy elision nie zachodziło i występowały niejawne wywołania dodatkowych konstruktorów z powodu specyfiki ABI.