Как построить объект std::array со списком инициализаторов?

Возможный дубликат:
Как инициализировать массив элементов с помощью initializer_list?

Вы можете просто создать std::array со списком инициализаторов:

std::array<int, 3> a = {1, 2, 3};  // works fine

Однако, когда я пытаюсь построить его из std::initializer_list в качестве члена данных или базового объекта в классе, это не работает:

#include <array>
#include <initializer_list>

template <typename T, std::size_t size, typename EnumT>
struct enum_addressable_array : public std::array<T, size>
{
    typedef std::array<T, size> base_t;
    typedef typename base_t::reference reference;
    typedef typename base_t::const_reference const_reference;
    typedef typename base_t::size_type size_type;

    enum_addressable_array(std::initializer_list<T> il) : base_t{il} {}

    reference operator[](EnumT n)
    {
        return base_t::operator[](static_cast<size_type>(n));
    }

    const_reference operator[](EnumT n) const
    {
        return base_t::operator[](static_cast<size_type>(n));
    }
};

enum class E {a, b, c};
enum_addressable_array<char, 3, E> ea = {'a', 'b', 'c'};

Ошибки с gcc 4.6:

test.cpp: In constructor 'enum_addressable_array<T, size, EnumT>::enum_addressable_array(std::initializer_list<T>) [with T = char, unsigned int size = 3u, EnumT = E]':
test.cpp:26:55:   instantiated from here
test.cpp:12:68: error: no matching function for call to 'std::array<char, 3u>::array(<brace-enclosed initializer list>)'
test.cpp:12:68: note: candidates are:
include/c++/4.6.1/array:60:12: note: std::array<char, 3u>::array()
include/c++/4.6.1/array:60:12: note:   candidate expects 0 arguments, 1 provided
include/c++/4.6.1/array:60:12: note: constexpr std::array<char, 3u>::array(const std::array<char, 3u>&)
include/c++/4.6.1/array:60:12: note:   no known conversion for argument 1 from 'std::initializer_list<char>' to 'const std::array<char, 3u>&'
include/c++/4.6.1/array:60:12: note: constexpr std::array<char, 3u>::array(std::array<char, 3u>&&)
include/c++/4.6.1/array:60:12: note:   no known conversion for argument 1 from 'std::initializer_list<char>' to 'std::array<char, 3u>&&'

Как я могу заставить его работать, чтобы мой класс-оболочка мог быть инициализирован с помощью списка инициализаторов, как таковой:

enum_addressable_array<char, 3, E> ea = {'a', 'b', 'c'};

person HighCommander4    schedule 01.08.2011    source источник


Ответы (3)


У std::array<> нет конструктора, который принимает std::initializer_list<> (конструктор списка инициализаторов), и нет специальной языковой поддержки того, что может означать передача std::initializer_list<> конструкторам класса, чтобы это могло работать. Так что это не удается.

Чтобы это работало, ваш производный класс должен перехватывать все элементы, а затем пересылать их в шаблон конструктора:

template<typename ...E>
enum_addressable_array(E&&...e) : base_t{{std::forward<E>(e)...}} {}

Обратите внимание, что в этом случае вам нужно {{...}}, потому что исключение фигурных скобок (исключение фигурных скобок, как в вашем случае) не работает в этом месте. Это разрешено только в объявлениях формы T t = { ... }. Поскольку std::array<> состоит из структуры, включающей в себя необработанный массив, для этого потребуется два уровня фигурных скобок. К сожалению, я полагаю, что точная совокупная структура std::array<> не указана, поэтому вам нужно надеяться, что она работает в большинстве реализаций.

person Johannes Schaub - litb    schedule 01.08.2011
comment
Clang жалуется, что ввод должен быть E&& ...e. (И иногда g++ говорит мне убивать людей.) - person John McFarlane; 15.03.2013
comment
Разве это не перезапишет конструктор копирования и тому подобное? Можно ли ограничить больше? Например, template<typename ...E, class = decltype(base_t{{std::forward<Args__>(std::declval<Args__>()...)...}}> enum_addressable_array(E&&...e) : base_t{{std::forward<E>(e)...}} {} (у меня не получается) - person alfC; 08.07.2013
comment
@alfC Шаблон никогда не будет лучше соответствовать, чем неявно сгенерированные специальные функции-члены, поскольку они являются точными совпадениями (даже если они неявно или явно объявлены удаленными). Сложность, однако, заключается в том, что это будет лучшим совпадением (и, следовательно, предпочтительным в разрешении перегрузки), если вы передадите что-то вроде enum_addressable_array &. - person David Stone; 24.11.2013
comment
У меня есть вопрос. Вы говорите: Unfortunately, I believe that the exact aggregate structure of std::array<> is unspecified, so you will need to hope that it works on most implementations. Однако согласно en.cppreference.com/w/ cpp/concept/SequenceContainer#cite_note-1 , std::array требуется для поддержки назначения braced-init-list. Не означает ли это, что ваше решение должно работать в соответствии со стандартом C++, и вам не нужно полагаться на детали реализации? Я спрашиваю, а не говорю, что это должно работать: этот уровень продвижения находится на границе моего понимания :) - person ; 15.11.2016
comment
@gaazkam требуется поддержка = { ... } инициализации. Но не обязательно поддерживать = {{ .. }} инициализацию. Тем не менее, для случая инициализатора члена это будет работать только с последним. Но поскольку поддержка последнего не требуется, а поддержка первого требуется только для синтаксиса = { ... }, в конце концов у вас нет переносимого синтаксиса в случае инициализатора члена. Тем не менее, я не в курсе последних поправок к спецификации. Возможно, они и сейчас поддерживают это дело. - person Johannes Schaub - litb; 16.11.2016

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

std::array<int, 4> my_array = {{1, 2, 3, 4}};

Обратите внимание, что здесь не используется std::initializer_list... это просто использование списка инициализаторов C++ для инициализации общедоступного элемента массива std::array.

person Jason    schedule 01.08.2011

У std::array нет конструктора, который принимает std::initializer_list. Это хорошо, потому что списки инициализаторов могут быть больше, чем фиксированный размер массива.

Вы можете инициализировать его, проверив, что список инициализаторов не превышает размер массива, а затем скопировав элементы списка инициализаторов в elems член std::array с std::copy.

person R. Martinho Fernandes    schedule 01.08.2011