Синтаксис списка инициализаторов в списке инициализаторов членов с использованием C++11
Я проходил " Путешествие по C++", и Бьярне использует функцию списка инициализаторов C++11 при инициализации членов в конструкторе, например так (используя фигурные скобки):
A a;
B b;
Foo(Bar bar):
a{bar.a}, b{bar.b}
{}
Это, однако, не компилируется до C++11. В чем разница со старым списком инициализирующих элементов (с использованием круглых скобок):
Foo(Bar bar):
a(bar.a), b(bar.b)
{}
Так в чем же разница и когда один должен быть предпочтительнее другого?
3 ответа
Так в чем же разница?
Круглые скобки работают только для типов, не относящихся к классам, или типов с подходящим конструктором для количества аргументов в скобках.
На них работают волнистые брекеты, а также на агрегаты - просто struct
или типы массивов без конструктора. Так что будет работать следующее:
struct {
int a,b;
} aggregate;
int array[2];
Foo() : aggregate{1,2}, array{3,4} {}
Наконец, фигурные скобки будут соответствовать конструктору, берущему подходящий тип initializer_list
, а не конструктор с параметром (ами), чтобы соответствовать аргументам. Например:
std::vector<int> v1;
std::vector<int> v2;
Foo() :
v1(10,2), // 10 elements with value 2
v2{10,2} // 2 elements with value 10,2
{}
когда один должен быть предпочтительнее другого?
Предпочитайте круглые скобки, если хотите прояснить, что при инициализации используется конструктор, а не агрегат или initializer_list
; или для принудительного использования определенного конструктора.
Предпочитайте фигурные скобки, когда вам нужна форма инициализации, которая не поддерживается иным образом; или когда вы просто хотите, чтобы инициализация "сделала правильные вещи".
В тех случаях, когда оба делают одно и то же, выбор в значительной степени эстетичен.
Может быть разница в нескольких действительно раздражающих крайних случаях:
std::vector<int> v{3, 2}; // constructs a vector containing [3, 2]
std::vector<int> u(3, 2); // constructs a vector containing [2, 2, 2]
Это верно независимо от того, v
а также u
являются просто переменными в функции или являются членами класса, инициализированного в списке инициализации.
Но за исключением случаев, когда std::initializer_list<T>
конструктор перекрывается с обычным конструктором, принимающим такое же количество аргументов, различий нет.
Краткое описание: обозначение в списке инициализатора члена совпадает с обозначением переменных, инициализированных в другом месте. К сожалению, описание того, что он делает, совсем не так просто, потому что есть два противоречивых изменения, связанных с использованием фигурных скобок для вызовов конструктора:
- Унифицированный синтаксис инициализации был предназначен для того, чтобы во всех конструкциях использовались фигурные скобки, и он просто вызывал бы соответствующий конструктор, даже если это аргумент по умолчанию или тип вообще не имеет конструктора и используется прямая инициализация.
- Для поддержки переменного числа аргументов можно использовать фигурные скобки, чтобы
std::initializer_list<T>
без дополнительной пары скобок / фигурных скобок. Если есть конструктор, принимающийstd::initializer_list<T>
(для подходящего типаT
) этот конструктор используется при использовании фигурных скобок.
Поставить по другому, если нет std::initializer_list<T>
конструктор, но какой-то другой определяемый пользователем конструктор использование скобок и фигурных скобок эквивалентно. В противном случае это вызывает std::initializer_list<T>
конструктор. ... и я предполагаю, что мне не хватает нескольких деталей, поскольку вся инициализация на самом деле довольно сложна.