Каковы плюсы и минусы std :: initializer_list и c array []? - PullRequest
8 голосов
/ 16 мая 2019

Предположим, у меня есть некоторая гипотетическая структура:

struct X {
  int i;
  double d;
}

Затем я могу написать

constexpr X x_c_array[]{{5, 6.3}};

или

constexpr std::initializer_list<X> x_ilist{{5, 6.3}};

Использование auto невозможен - компилятор должен знать внутренний тип .

Есть ли минусы у какой-либо версии?

Обновление:

Также вызывает беспокойство, если вы сможете использовать / преобразовать один тип в другой тип - например.при конструировании стандартных контейнеров?

Ответы [ 3 ]

5 голосов
/ 16 мая 2019

Просто и ясно: initializer_list не является контейнером. Это неизменный взгляд на внешние элементы. Это совершенно не подходит для любого сценария, в котором контейнер был бы полезен - рассмотрите ненужную косвенность (без изменения размера), неизменность, идиоматичность его имени. Кроме того, он не имеет надлежащего интерфейса.

Ситуация, когда оба кажутся адекватными, является параметром конструктора для последовательности. Если длина является фиксированной (или параметризованной шаблоном), то int const (&arr)[N] возможен, хотя initializer_list намного проще и более гибок. В конце концов, именно для этого он был разработан и предназначен ..

2 голосов
/ 16 мая 2019

Как написано в комментариях, это широкий аргумент.

Во всяком случае, я обращаю ваше внимание на точку.

В первом случае

X x1[] {{5, 6.3}};

номер элемента x1 является частью типа x1.

Итак, у вас есть это

X x1[] {{5, 6.3}};
X x2[] {{5, 6.3}, {7, 8.1}};

static_assert( false == std::is_same<decltype(x1), decltype(x2)>::value );

Использование списка инициализаторов

std::initializer_list<X> x3 {{5, 6.3}};
std::initializer_list<X> x4 {{5, 6.3}, {7, 8.1}};

static_assert( true == std::is_same<decltype(x3), decltype(x4)>::value );

тип остается прежним при изменении количества элементов.

В зависимости от ваших потребностей это может быть преимуществом для первого или второго решения.

Тот факт, что количество элементов является частью типа для массивов в стиле C, может быть небольшим преимуществом в метапрограммировании.

Предположим, вам нужна функция, которая возвращает сумму i значений массивов, с массивом в стиле C вы можете написать

template <std::size_t N, std::size_t ... Is>
constexpr auto sum_i_helper (X const (&xArr)[N], std::index_sequence<Is...>)
 { return (... + xArr[Is].i); }

template <std::size_t N>
constexpr auto sum_i (X const (&xArr)[N])
 { return sum_i_helper(xArr, std::make_index_sequence<N>{}); }

и эта функция компилируется также, когда аргумент sum_i() является значением not-constexpr.

Если вы хотите написать что-то похожее с std::initializer_list, это немного сложнее, потому что size() списка не обязательно является известным значением времени компиляции, или вы передаёте его как параметр шаблона (но функция не не работает со списками времени выполнения) или вы используете size() внутри функции, но вы не можете использовать его для инициализации std::index_sequence.

В любом случае, со списком инициализаторов вы можете использовать старый добрый for() цикл

constexpr auto sum_i (std::initializer_list<X> const lx)
 { 
   int ret { 0 };

   for ( auto const & x : lx )
      ret += x.i;

   return ret;
 }

и функция может вычислять время компиляции, когда lx является значением constexpr.

Также беспокоит, сможете ли вы использовать / преобразовать один тип в другой, например. при конструировании стандартных контейнеров?

Преобразование массива в список инициализатора, это легко работает как с известным временем компиляции, так и с временем выполнения

template <std::size_t N, std::size_t ... Is>
constexpr auto convertX_h (X const (&xArr)[N], std::index_sequence<Is...>)
 { return std::initializer_list<X>{ xArr[Is]... }; }

template <std::size_t N>
constexpr auto convertX (X const (&xArr)[N])
 { return convertX_h(xArr, std::make_index_sequence<N>{}); }

// ....

X x1[] {{5, 6.3}};

std::initializer_list<X> x5 = convertX(x1);

Преобразование списка инициализатора в массив в стиле C сложнее, поскольку тип массива зависит от количества элементов, поэтому вам необходимо знать время компиляции количество элементов в списке инициализатора потому что вы не можете получить произвольный доступ к списку инициализаторов и, что еще хуже, потому что вы не можете написать функцию, которая возвращает массив в стиле C.

Я могу представить себе следующее решение, которое преобразует список инициализатора в std::array (не по теме: используйте std::array вместо массива в стиле C, когда это возможно)

template <std::size_t N>
constexpr auto convertX (std::initializer_list<X> const lx)
 { 
   std::array<X, N> ret;

   std::size_t i { 0u };

   for ( auto const & x : lx )
      ret[i++] = x;

   return ret;
 }

// ...

constexpr std::initializer_list<X> x4 {{5, 6.3}, {7, 8.1}};

auto x6 = convertX<x4.size()>(x4);

, но x6 теперь является std::array<X, 2>, а не X[2], а x4 должно быть constexpr.

1 голос
/ 16 мая 2019

Массив может быть неконстантным.initializer_list разрешает только постоянный доступ к элементам.В вашем примере вы используете constexpr и, следовательно, неявно const, так что в этом случае это не имеет значения.Но если вам нужен non-const, initializer_list не вариант.Это особенно раздражает в конструкторах initializer_list, где вы хотите удалить элементы из списка, но не можете, потому что объекты являются постоянными.

Массив стиля C может затухать, указывая на первый элемент, которыйновичков иногда смущают.initializer_list нет.Вместо этого вы можете использовать обертку массива, которая также не разрушается, но вам нужно либо указать тип и размер, либо использовать тип в списке инициализации фигурной скобки, чтобы разрешить вывод шаблона:

constexpr std::array x_std_array{X{5, 6.3}};

И,как более подробно рассмотрено в ответе max66, initializer_list может иметь любой размер, в то время как размер массива зависит от его типа, что является либо преимуществом, либо недостатком.Размерная часть типа является преимуществом в метапрограммировании шаблонов, в то время как «скрытый» размер является преимуществом, поскольку вам не нужен шаблон в первую очередь.

...