Direct-initialization
Инициализирует объект из явного набора аргументов конструктора.
Содержание |
Синтаксис
T
object
(
arg
);
T
object
|
(1) | ||||||||
T
object
{
arg
};
|
(2) | (since C++11) | |||||||
T
(
other
)
T
|
(3) | ||||||||
static_cast<
T
>(
other
)
|
(4) | ||||||||
new
T
(
args, ...
)
|
(5) | ||||||||
Class
::
Class
()
:
member
(
args, ...
)
{
...
}
|
(6) | ||||||||
[
arg
]() {
...
}
|
(7) | (since C++11) | |||||||
`, `
`, `
Объяснение
Прямая инициализация выполняется в следующих ситуациях:
Эффекты прямой инициализации:
-
Если
Tявляется типом массива,
|
(до C++20) |
struct A { explicit A(int i = 0) {} }; A a[2](A(1)); // OK: initializes a[0] with A(1) and a[1] with A() A b[2]{A(1)}; // error: implicit copy-list-initialization of b[1] // from {} selected explicit constructor |
(начиная с C++20) |
-
Если
Tявляется типом класса,
|
(начиная с C++17) |
-
-
конструкторы
Tанализируются, и наилучшее соответствие выбирается с помощью разрешения перегрузки. Затем конструктор вызывается для инициализации объекта.
-
конструкторы
struct B { int a; int&& r; }; int f(); int n = 10; B b1{1, f()}; // OK, lifetime is extended B b2(1, f()); // well-formed, but dangling reference B b3{1.0, 1}; // error: narrowing conversion B b4(1.0, 1); // well-formed, but dangling reference B b5(1.0, std::move(n)); // OK |
(since C++20) |
-
В противном случае, если
Tявляется неклассовым типом, но исходный тип - классовый, рассматриваются функции преобразования исходного типа и его базовых классов (при их наличии), и наилучшее соответствие выбирается с помощью разрешения перегрузки. Выбранное пользовательское преобразование затем используется для преобразования выражения инициализатора в инициализируемый объект. -
В противном случае, если
Tявляется bool , а исходный тип - std::nullptr_t , значение инициализируемого объекта равно false . -
В противном случае,
стандартные преобразования
используются, если необходимо, для преобразования значения
other
в версию
Tбез cv-квалификаторов, и начальное значение инициализируемого объекта является (возможно преобразованным) значением.
Примечания
Прямая инициализация более разрешительна, чем копирующая инициализация: копирующая инициализация рассматривает только не- explicit конструкторы и неявные пользовательские conversion functions , тогда как прямая инициализация рассматривает все конструкторы и все пользовательские функции преобразования.
В случае неоднозначности между объявлением переменной с использованием синтаксиса прямой инициализации (1) (с круглыми скобками) и объявлением функции , компилятор всегда выбирает объявление функции. Это правило разрешения неоднозначности иногда оказывается неинтуитивным и получило название most vexing parse .
#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>()); // Исправление для после C++11 (для объявления переменной) - использовать list-инициализацию для любого // из аргументов: 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("test"); // конструктор из 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'; }
Вывод:
test aaaaaaaaaa 1 2