Прямая инициализация — cppreference.com
Материал из cppreference.com
Инициализирует объект из явного набора аргументов конструктора.
Синтаксис
T объект ( аргумент );
T объект |
(1) | ||||||||
T объект { аргумент };
|
(2) | (начиная с C++11) | |||||||
T ( другой )
T |
(3) | ||||||||
static_cast< T >( другой )
|
(4) | ||||||||
new T(аргументы, ...)
|
(5) | ||||||||
Класс::Класс() : элемент(аргументы, ...) { ... }
|
(6) | ||||||||
[аргумент](){ ... }
|
(7) | (начиная с C++11) | |||||||
Объяснение
Прямая инициализация выполняется в следующих случаях:
1) Инициализация непустым списком выражений в скобках или списками инициализации в фигурных скобках (начиная с C++11).
2) Инициализация объекта неклассового типа с помощью одного инициализатора, заключённого в фигурные скобки (примечание: типы классов и другие варианты использования списка инициализации в фигурных скобках смотрите в разделе инициализация списком) (начиная с C++11).
3) Инициализация prvalue временным объектом (до C++17)результирующего объекта prvalue (начиная с C++17) с помощью приведения в стиле функции или с помощью списка выражений в скобках.
4) Инициализация prvalue временного объекта (до C++17)результирующего объекта prvalue (начиная с C++17) выражением static_cast.
5) Инициализация объекта с динамической длительностью хранения выражением new с инициализатором.
6) Инициализация базового объекта или нестатического элемента списком инициализаторов конструктора.
7) Инициализация элементов объекта замыкания из переменных, захваченных копированием в лямбда-выражении.
Эффекты прямой инициализации:
- Если
Tявляется типом массива,
|
(до C++20) |
struct A { explicit A(int i = 0) {} }; A a[2](A(1)); // OK: инициализирует a[0] с помощью A(1) и a[1] с помощью A() A b[2]{A(1)}; // ошибка: неявная инициализация списком копирования b[1] // из {} выбранного явного конструктора |
(начиная с C++20) |
- Если
Tявляется типом класса,
|
(начиная с C++17) |
- проверяются конструкторы
T, и с помощью разрешения перегрузки выбирается наилучшее совпадение. Затем для инициализации объекта вызывается конструктор.
- проверяются конструкторы
struct B { int a; int&& r; }; int f(); int n = 10; B b1{1, f()}; // OK, продлевается время жизни B b2(1, f()); // верно, но висячая ссылка B b3{1.0, 1}; // ошибка: сужающее преобразование B b4(1.0, 1); // верно, но висячая ссылка B b5(1.0, std::move(n)); // OK |
(начиная с C++20) |
- Иначе, если
Tявляется неклассовым типом, но исходный тип является классовым, проверяются функции преобразования исходного типа и его базовых классов, если таковые имеются, и путём разрешения перегрузки выбирается наилучшее совпадение. Затем выбранное определяемое пользователем преобразование используется для преобразования выражения инициализатора в инициализируемый объект. - Иначе, если
Tравноboolа исходный тип это std::nullptr_t, значением инициализированного объекта будетfalse. - Иначе при необходимости используются стандартные преобразования для преобразования значения другой в cv-неквалифицированную версию
T, а начальным значением инициализируемого объекта является (возможно преобразованное) значение.
Примечание
Прямая инициализация более разрешительна, чем инициализация копированием: при инициализации копированием учитываются только конструкторы, не являющиеся явными, и неявные определяемые пользователем функции преобразования, а при прямой инициализации учитываются все конструкторы и все определяемые пользователем функции преобразования.
В случае неоднозначности между объявлением переменной с использованием синтаксиса прямой инициализации (1) (с круглыми скобками) и объявлением функции компилятор всегда выбирает объявление функции. Это правило устранения неоднозначности иногда противоречит здравому смыслу и называется самый неприятный синтаксический анализ.
#include <fstream> #include <iterator> #include <string> int main() { std::ifstream file("data.txt"); // Следующее это объявление функции: std::string foo1(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()); // Оно объявляет функцию с именем foo1, тип возвращаемого значения которой std::string, // первый параметр имеет тип std::istreambuf_iterator<char> и имя "file", // второй параметр не имеет имени и имеет тип std::istreambuf_iterator<char>(), // который переписывается в тип указателя на функцию // std::istreambuf_iterator<char>(*)() // Исправление до C++11 (для объявления переменной) — добавьте // дополнительные круглые скобки вокруг одного из аргументов: std::string str1( (std::istreambuf_iterator<char>(file) ), std::istreambuf_iterator<char>()); // Исправление после С++11 (для объявления переменной) — используйте // инициализацию списком для любого из аргументов: std::string str2(std::istreambuf_iterator<char>{file}, {}); }
Пример
#include <iostream> #include <memory> #include <string> struct Foo { int mem; explicit Foo(int n) : mem(n) {} }; int main() { std::string s1("тест"); // конструктор из const char* std::string s2(10, 'a'); std::unique_ptr<int> p(new int(1)); // OK: разрешены явные конструкторы // std::unique_ptr<int> p = new int(1); // ошибка: конструктор явный Foo f(2); // f инициализируется напрямую: // параметр конструктора n инициализируется копированием из rvalue 2 // f.mem инициализируется напрямую из параметра n // Foo f2 = 2; // ошибка: конструктор явный std::cout << s1 << ' ' << s2 << ' ' << *p << ' ' << f.mem << '\n'; }
Вывод: