Рассмотрим следующую демонстрационную программу.
#include <iostream>
struct A
{
int x;
explicit A( int x = 0 ) : x( x ) {}
};
int main()
{
A a1 { 10 };
std::cout << "a1.x = " << a1.x << '\n';
// A a2 = { 10 };
}
В этом объявлении
A a1 { 10 };
используется прямая инициализация.
И в закомментированном объявлении
// A a2 = { 10 };
, который также может быть переписан как
// A a2 = 10;
, там используется инициализация копирования.Но конструктор объявлен со спецификатором explicit
.Таким образом, компилятор выдаст ошибку.То есть он не может преобразовать целочисленный объект 10
в тип A
неявно.
Вместо этого можно написать
A a2 = A{ 10 };
То есть вызов конструктора явно.
Конечно, для фундаментальных типов нет никакой разницы, потому что ни один конструктор не применяется, за исключением того, что суженное преобразование не допускается, когда используется фигурная инициализация, как, например,
int x { 1.0 };
Существует большая разница, когдаспецификатор типа является заполнителем auto
.
Например,
auto x = 10;
x
имеет тип int
.
auto x { 10 };
x
снова имееттип int
.
auto x = { 10 };
Теперь x
имеет тип std::initializer_list<int>
.
Например, вы можете переписать ваш цикл
for (unsigned int counter{ 1 }; counter <= 20; ++counter) {
следующимway
for ( auto counter{ 1u }; counter <= 20; ++counter) {
но вы не можете писать
for ( auto counter = { 1u }; counter <= 20; ++counter) {
, потому что в этом случае тип переменной counter
равен std::initializer_list<unsigned int>
.
Так что в общему вас есть следующие формы инициализации
T x = value;
T x = { value };
T x( value );
T x { value };
Например
#include <iostream>
int main()
{
int x1 = 1;
std::cout << "x1 = " << x1 << '\n';
int x2 = ( 2 );
std::cout << "x2 = " << x2 << '\n';
int x3( 3 );
std::cout << "x3 = " << x3 << '\n';
int x4{ 4 };
std::cout << "x4 = " << x4 << '\n';
}
Вывод программы:
x1 = 1
x2 = 2
x3 = 3
x4 = 4
Но есть еще одна ситуация, когда вместо T()
вы должны использовать T{}
в качестве инициализатора.Это когда используются шаблонные функции.
Рассмотрим следующую демонстрационную программу
#include <iostream>
template <class>
void f()
{
std::cout << "f<T>() is called\n";
}
template <int>
void f()
{
std::cout << "f<int>() is called\n";
}
int main()
{
f<int()>();
f<int{}>();
}
Ее вывод
f<T>() is called
f<int>() is called
Конструкция int()
, используемая в качестве аргумента шаблоназадает аргумент шаблона типа int
, а конструкция int{}
используется в качестве аргумента шаблона; специфицирует аргумент шаблона нетипичного типа int
, равный 0
.