Где мы можем использовать инициализацию списка?

Этот вопрос уже содержит информацию о том, что такое POD и агрегаты, и приводятся некоторые примеры по агрегатной инициализации.

Вопрос здесь , где вы можете использовать инициализацию списка?

Также , где вы можете использовать (при отсутствии лучшего термина) назначение списка?

Ответ должен иметь дело как с С++ 03, так и с С++ 11, подчеркивая различия между ними.

Ответ 1

С++ 03

Инициализация списка

В С++ 03 вы можете использовать только инициализацию списка для агрегатов (С++ 03 [dcl.init.aggr]) и скалярные (С++ 03 [dcl.init]/13) типы:

int i = { 0 };
POD pod = { 0, 1, 2 };

Назначение списка

Вы не можете использовать "назначение списка" в любом месте на С++ 03. Грамматика, показанная в [expr.ass]/1, не позволяет скопировать список справа от присваивания.

С++ 11

Инициализация списка

В С++ 11 вы можете использовать инициализацию списка почти везде, где вы можете создать переменную (см. [dcl.init] в С++ 11 и [dcl.init.list]/1, в которой перечислены контексты, инициализация разрешена) например

struct Base { };

struct Class : Base
{
    int mem{ 0 };  // init non-static data member

    Class(int i)
    : Base{}   // init base class
    , mem{i}   // init member
    {
      int j{i};   // init local var

      int k = int{0};  // init temporary

      f( { 1 } );  // init function arg

      int* p = new int{1};  // new init

      // int k(int());  // most vexing parse, declares function
      int k{ int{} };   // ok, declares variable

      int i[4]{ 1,2,3,4 };   // init array
    }

    Class f(int i)
    {
      return { i };   // init return value
    }
};

Class c{1};   // init global var

Большинство инициализаций выше объявляют int или массив int, но тот же синтаксис может использоваться для вызова конструктора для типа класса (например, две строки, которые строят переменную Class)

Как и в любом контексте, где вы можете инициализировать переменную, инициализация списка также хорошо взаимодействует с другой новой функцией С++ 11: шаблона класса std::initializer_list. Конструктор, который принимает аргумент std::initializer_list, может быть передан произвольно длинный список значений, который конструктор может перебирать через begin() и end() функции-члены из std::initializer_list. Основное преимущество этой новой функции заключается в том, что она позволяет инициализировать контейнер с набором элементов, например. vector<int> v{ 0, 1, 2, 3, 4, 5 } вместо того, чтобы конструировать контейнер, а затем вставлять значения.

Инициализация списка также может использоваться для элементов в списке с расширенным списком, позволяя вложенную инициализацию списка, например. Map m{ {a, b}, {c, d} }, а не Map m{ Map::value_type(a, b), Map::value_type(c, d) }

Единственная инициализация списка времени не делает правильную вещь, когда вы пытаетесь построить тип класса, вызывая конструктор, если класс имеет другой конструктор, принимающий std::initializer_list, поскольку инициализация списка всегда будет предпочтительнее принимать конструктор a std::initializer_list eg

// attempts to create vector of 5 elements, [1,1,1,1,1]
// but actually creates a vector with two elements, [5,1] 
std::vector<int> v{ 5, 1 };

Это не вызывает конструктор vector(size_type, const int&), а не вызывает конструктор vector(initializer_list<int>).

Назначение списка

В С++ 11 вы можете использовать "list-assign"

  • при назначении скалярному типу, если в списке бит-init есть один элемент, который можно конвертировать (без сужения) в тип переменной (см. [expr.ass]/9)
  • когда левый операнд присваивания является типом класса с определяемым пользователем оператором присваивания, и в этом случае бит-init-list используется для инициализации аргумента оператора (см. [expr.ass]/9). Это включает в себя как случаи, такие как operator=(std::initializer_list<T>), где элементы списка braced-init-list в правом операнде конвертируются в T, например. для std::vector<int> v выше, v = { 1, 2, 3 } заменит содержимое контейнера на [1,2,3], и когда список braced-init может быть неявно преобразован в тип аргумента оператора, через подходящий конструктор, например

    struct A {
      int i;
      int j;
    };
    
    struct B {
      B& operator=(const A&);
    };
    
    int main() {
      B b;
      b = { 0, 1 };
    }
    

    В последней строке main бит-init-list будет неявно преобразован во временный A, тогда оператор присваивания B будет вызван с таким временным аргументом.

Ответ 2

Агрегатная инициализация - это подмножество инициализации списка, которая ограничена только агрегатами и POD (как обсуждалось в вопросе, на который вы ссылались). Оба типа инициализации используют фигурные скобки и необязательно и равны, поэтому синтаксис кажется одинаковым в точке инициализации. Подробнее см. http://en.cppreference.com/w/cpp/language/aggregate_initialization и http://en.cppreference.com/w/cpp/language/list_initialization, в том числе где каждая форма инициализации может быть использована.

В С++ 03 агрегатная инициализация может использоваться только с equals (т.е. T object {arg1, arg2}; недействительна только T object = {arg1, arg2};), а С++ 11 разрешает ее без равно (т.е. объект T {arg1, arg2}, стал действительным). Также в С++ 11 агрегатная инициализация была слегка изменена, чтобы запретить сужение конверсий в агрегатной инициализации.

Подмножество инициализации списка, которое не является подмножеством агрегатной инициализации, было введено в С++ 11.

Ответ 3

Инициализация списка может использоваться для инициализации динамически распределенных массивов (С++ 11):

int * a = new int[3] {4, 3, 2};

Очень отличная функция, недоступная в С++ 03.