В C++ различают прямую (direct) и косвенную (copy) инициализацию объектов:
Прямая инициализация:
MyClass obj(arg1, arg2); int x(5);
Для классов вызывает подходящий конструктор напрямую.
Косвенная инициализация:
MyClass obj = MyClass(arg1, arg2); int x = 5;
Это создаёт временный объект, а затем копирует (или перемещает) его (использует копирующий/перемещающий конструктор).
Различия:
Пример:
struct NonCopyable { NonCopyable(int) {} NonCopyable(const NonCopyable&) = delete; }; NonCopyable a(5); // ОК: прямое NonCopyable b = NonCopyable(5); // ОК с оптимизацией, ошибка до С++17 без неё NonCopyable c = 5; // Ошибка: нет конструктора копирования
"Может ли copy-initialization вызвать больше вызовов конструкторов, чем direct-initialization, если используется C++11 и с включённым оптимизирующим компилятором?"
Ответ: Теоретически — да. Без copy elision copy-initialization создаёт временный объект, затем вызывает конструктор копирования или перемещения. Direct-initialization всегда вызывает только основной конструктор. Однако современные компиляторы с включённой оптимизацией (C++17 и выше) устраняют это различие благодаря guaranteed copy elision.
История
В финансовом проекте использовали copy-initialization с временным объектом, а нужный тип не имел конструктора копирования. Приложение не собиралось на старых компиляторах (до C++17), хотя direct-init работал.
История
В утилите сериализации данных программисты считали, что direct-init и copy-init эквивалентны. В результате возникали лишние копирования больших структур и провалы по производительности.
История
При инициализации глобальных массивов пользовательских объектов использовали косвенную инициализацию. Ошибки проявлялись только на одной платформе, где copy elision не происходило, и возникали неявные вызовы лишних конструкторов из-за особенностей ABI.