Можно ли инициализировать массив, используя 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;
для инициализации массива?
Ответы
Ответ 1
Другие ответили правильно, сказав, что это невозможно заранее. Но с небольшими помощниками вы можете получить довольно близко
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 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 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
}
Обратите внимание, что как нестатический случай в верхней части ответа, так и случай "головного стола" проверяют только то, предоставили ли вы слишком мало элементов инициализации, а затем ошибки в случае initializer_list
. Если вы предоставили слишком много для случая initializer_list
, конечные элементы просто игнорируются.
Ответ 2
Насколько я знаю, нет: вы не можете инициализировать std::array
с помощью std::initializer_list
.
Проблема заключается в том, что std::array
предназначен как легкая замена (обертка) для классического массива C-style. Так что свет, который не имеет конструкторов, поэтому может использоваться только неявный конструктор.
Конструкция с агрегатной инициализацией (через неявный конструктор) возможна, потому что это возможно для массива 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.: Извините за мой плохой английский.
Ответ 3
Проблема с std::array
заключается в том, что он должен быть агрегатным типом, поэтому он не имеет конструкторов.
Следовательно, возможны только агрегатная инициализация или тривиальная копия.
std::initializer_list
- это класс, отличный от std::array
, поэтому требуется (неявное) неявное преобразование.
См. http://en.cppreference.com/w/cpp/language/aggregate_initialization
и http://en.cppreference.com/w/cpp/container/array
для справки.