Могу ли я инициализировать массив, используя std::initializer_list вместо инициализатора, заключенного в фигурные скобки?

Могу ли я инициализировать массив, используя объект std::initializer_list вместо инициализатора, заключенного в фигурные скобки?

Как известно, мы можем сделать это: http://en.cppreference.com/w/cpp/language/aggregate_initialization< /а>

unsigned char b[5]{"abc"};
// equivalent to unsigned char b[5] = {'a', 'b', 'c', '\0', '\0'};

int ar[] = {1,2,3};
std::array<int, 3> std_ar2{ {1,2,3} };    // std::array is an aggregate
std::array<int, 3> std_ar1 = {1, 2, 3};

Но я не могу инициализировать массив с помощью std::initializer_list il;:

http://ideone.com/f6aflX

#include <iostream>
#include <initializer_list>
#include <array>

int main() {

    int arr1[] =  { 1, 2, 3 };  // OK
    std::array<int, 3> arr2 =  { 1, 2, 3 }; // OK

    std::initializer_list<int> il = { 1, 2, 3 };
    constexpr std::initializer_list<int> il_constexpr = { 1, 2, 3 };

    //int arr3[] = il;  // error
    //int arr4[] = il_constexpr;    // error

    //std::array<int, 3> arr5 =  il;    // error
    //std::array<int, 3> arr6 =  il_constexpr;  // error

    return 0;
}

Но как я могу использовать std::initializer_list il; для инициализации массива?


person Alex    schedule 13.08.2016    source источник
comment
Repoened.. обманутый был общий вопрос об инициализации массива элементов с { .. }. Этот просит инициализировать тип std:: (в него не допускаются изменения!) с помощью std::initializer_lists. Это совершенно разные вопросы.   -  person Johannes Schaub - litb    schedule 13.08.2016
comment
@Alex Разве вы не можете использовать copy из algorithm (пример)?   -  person skypjack    schedule 13.08.2016


Ответы (3)


Другой правильно ответил, что это невозможно заранее. Но с маленькими помощниками вы можете подобраться довольно близко

template<typename T, std::size_T N, std::size_t ...Ns>
std::array<T, N> make_array_impl(
    std::initializer_list<T> t,
    std::index_sequence<Ns...>) 
{
    return std::array<T, N>{ *(t.begin() + Ns) ... };
}

template<typename T, std::size_t N>
std::array<T, N> make_array(std::initializer_list<T> t) {
    if(N > t.size())
       throw std::out_of_range("that's crazy!");
    return make_array_impl<T, N>(t, std::make_index_sequence<N>());
}

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

template<typename T, std::size_t N>
struct ArrayInitializer {
    template<typename U> struct id { using type = U; };
    std::array<T, N> t;

    template<typename U = std::initializer_list<T>>
    ArrayInitializer(typename id<U>::type z) 
        :ArrayInitializer(z, std::make_index_sequence<N>())
    { 
        if(N > z.size())
            throw std::out_of_range("that's crazy!");
    }

    template<typename ...U>
    ArrayInitializer(U &&... u)
       :t{ std::forward<U>(u)... }
    { }

private:
    template<std::size_t ...Ns>
    ArrayInitializer(std::initializer_list<T>& t,
                     std::index_sequence<Ns...>)
       :t{ *(t.begin() + Ns) ... }
    { }
};

template<typename T, std::size_t N>
std::array<T, N> f(ArrayInitializer<T, N> ai) {
    return std::move(ai.t);
}

int main() {
   f<int, 5>({1, 2, 3, 4, 5});  // OK 
   f<int, 5>({1, 2, 3, 4, 5, 6});  // "too many initializers for array<int, 5>"

   std::initializer_list<int> il{1, 2, 3, 4, 5};
   f<int, 5>(il); // ok
}

Обратите внимание, что как нестатический случай в верхней части ответа, так и случай «head-desk» проверяют только то, предоставляете ли вы слишком мало инициализирующих элементов, а затем выдаете ошибки для случая initializer_list. Если вы укажете слишком много для случая initializer_list, конечные элементы просто будут проигнорированы.

person Johannes Schaub - litb    schedule 13.08.2016
comment
Спасибо! Да, можно использовать оба примера: ideone.com/s0ciUW и ideone.com/fbnM6W - person Alex; 13.08.2016

Насколько я знаю, нет: вы не можете инициализировать std::array с помощью std::initializer_list.

Проблема в том, что std::array задуман как облегченная замена (оболочка) классического массива в стиле C. Настолько легкий, что без конструкторов, поэтому можно использовать только неявный конструктор.

Возможна конструкция с агрегатной инициализацией (через неявный конструктор), потому что это возможно для массива в стиле C.

Но std::initializer_list — это класс, более сложный, чем агрегатная инициализация.

Например, вы можете инициализировать std::vector с помощью std::initializer_list, но только потому, что для std::vector существует явный конструктор, который получает std::initializer_list. Но std::vector - более тяжелый класс.

Единственное решение, которое я вижу, - это двухэтапный путь: (1) построение и (2) копирование значений std::initializer_list. Что-то типа

std::array<int, 3> arr5;

auto  ui = 0U;
auto  cit = il.cbegin();

while ( (ui < arr5.size()) && (cit != il.cend()) )
   arr5[ui++] = *cit++;

p.s.: извините за мой плохой английский.

person max66    schedule 13.08.2016
comment
Плохой английский? Вы пишете лучше, чем большинство моих коллег. Вам не нужны такие комментарии в ответах или вопросах о переполнении стека. Сообщество отредактирует все, что непонятно. - person Andon M. Coleman; 13.08.2016
comment
@ AndonM.Coleman - спасибо; Я постараюсь игнорировать мою языковую неуверенность. - person max66; 13.08.2016

Проблема с std::array заключается в том, что он должен быть агрегатным типом, поэтому у него нет конструкторов.

Следовательно, возможны только совокупная инициализация или тривиальное копирование. std::initializer_list — это класс, отличный от std::array, поэтому требуется (отсутствующее) неявное преобразование.

См. http://en.cppreference.com/w/cpp/language/aggregate_initialization и http://en.cppreference.com/w/cpp/container/array для справки.

person Emilio Garavaglia    schedule 13.08.2016
comment
@chris: Извините, не POD, а агрегированный тип. См. en.cppreference.com/w/cpp/language/aggregate_initialization и en.cppreference.com/w/cpp/container/array. Просто исправил текст - person Emilio Garavaglia; 15.08.2016