Пространства имён
Варианты
Действия

Прямая инициализация

Материал из cppreference.com
< cpp‎ | language
 
 
 
 

Инициализирует объект из явного набора аргументов конструктора.

Содержание

[править] Синтаксис

T объект ( аргумент );

T объект ( арг1, арг2, ... );

(1)
T объект { аргумент }; (2) (начиная с C++11)
T ( другой )

T ( арг1, арг2, ... )

(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 является типом класса,
  • если инициализатор представляет собой выражение prvalue, тип которого является тем же классом, что и T (игнорируя cv-квалификацию), само выражение инициализатора, а не материализованное из него временное выражение, используется для инициализации целевого объекта.
    (До C++17 компилятор мог исключить конструкцию из prvalue временного объекта в этом случае, но соответствующий конструктор должен быть доступен: смотрите пропуск копирования)
(начиная с C++17)
  • проверяются конструкторы T, и с помощью разрешения перегрузки выбирается наилучшее совпадение. Затем для инициализации объекта вызывается конструктор.
  • иначе, если целевой тип является агрегатным классом (возможно, cv-квалифицированным), он инициализируется, как описано в агрегатной инициализации, за исключением того, что сужающие преобразования разрешены, назначенные инициализаторы не разрешены, временная привязка к ссылке не продлевает время жизни, фигурные скобки не удаляются, а любые элементы без инициализатора инициализируются значением.
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

[править] Смотрите также