Прямая инициализация
Инициализирует объект из явного набора аргументов конструктора.
Содержание |
[править]Синтаксис
Tобъект( аргумент); Tобъект | (1) | ||||||||
Tобъект{ аргумент}; | (2) | (начиная с C++11) | |||||||
T( другой) T | (3) | ||||||||
static_cast< T>( другой) | (4) | ||||||||
new T( аргументы, ...) | (5) | ||||||||
Класс:: Класс() : элемент( аргументы, ...) { ...} | (6) | ||||||||
[ аргумент](){ ...} | (7) | (начиная с C++11) | |||||||
[править]Объяснение
Прямая инициализация выполняется в следующих случаях:
Эффекты прямой инициализации:
- Если
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';}
Вывод:
тест aaaaaaaaaa 1 2