Синтаксис списка инициализаторов в списке инициализаторов членов с использованием 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> конструктор перекрывается с обычным конструктором, принимающим такое же количество аргументов, различий нет.

Краткое описание: обозначение в списке инициализатора члена совпадает с обозначением переменных, инициализированных в другом месте. К сожалению, описание того, что он делает, совсем не так просто, потому что есть два противоречивых изменения, связанных с использованием фигурных скобок для вызовов конструктора:

  1. Унифицированный синтаксис инициализации был предназначен для того, чтобы во всех конструкциях использовались фигурные скобки, и он просто вызывал бы соответствующий конструктор, даже если это аргумент по умолчанию или тип вообще не имеет конструктора и используется прямая инициализация.
  2. Для поддержки переменного числа аргументов можно использовать фигурные скобки, чтобы std::initializer_list<T> без дополнительной пары скобок / фигурных скобок. Если есть конструктор, принимающий std::initializer_list<T> (для подходящего типа T) этот конструктор используется при использовании фигурных скобок.

Поставить по другому, если нет std::initializer_list<T> конструктор, но какой-то другой определяемый пользователем конструктор использование скобок и фигурных скобок эквивалентно. В противном случае это вызывает std::initializer_list<T> конструктор. ... и я предполагаю, что мне не хватает нескольких деталей, поскольку вся инициализация на самом деле довольно сложна.

Другие вопросы по тегам