Как создать std::array со списком инициализации без прямого указания размера

Как я могу a3 скомпилировать?

int main()
{
    int a1[] = { 1, 2, 3 };
    std::array<int, 3> a2 = { 1, 2, 3 };
    std::array<int> a3 = { 1, 2, 3 };
}

Очень неудобно и хрупко жестко задавать размер массива при использовании списка инициализации, особенно длинных. Есть ли работа вокруг? Я надеюсь на это, иначе я разочарован, потому что я ненавижу массивы C, и std::array должен быть их заменой.


person Neil Kirk    schedule 14.10.2014    source источник
comment
Вы можете реализовать свою собственную функцию make_array().   -  person 0x499602D2    schedule 14.10.2014
comment
Спасибо, полезно, может пригодится. Но когда такая ужасно сложная (для меня) функция требуется, чтобы сделать то, что С мог сделать 30 лет назад, это как-то кажется мне неправильным. Кроме того, я обеспокоен тем, что мой компилятор не будет достаточно умен, чтобы скомпилировать его точно так же, как я сам.   -  person Neil Kirk    schedule 14.10.2014
comment
C++17 представил руководства по дедукции для такого рода вещей, но вы должны опустить аргументы шаблона.   -  person bit2shift    schedule 26.10.2017


Ответы (2)


В настоящее время нет способа сделать это без создания собственного make_array, для этого есть предложение N3824: make_array со следующей областью действия:

LWG 851 ​​предназначен для замены синтаксиса

array<T, N> a = { E1, E2, ... };

, поэтому следующее

auto a = make_array(42u, 3.14);

правильно сформирован (с дополнительными static_casts внутри), потому что

array<double, 2> = { 42u, 3.14 };

хорошо сформирован.

В этой статье предполагается предоставить набор интерфейсов создания std::array, которые являются всеобъемлющими как с точки зрения кортежа, так и с точки зрения массива, поэтому сужение просто запрещено. См. более подробную информацию об этом направлении в разделе «Дизайн-решения».

Он также включает пример реализации, который довольно длинный, поэтому копирование здесь нецелесообразно, но у Конрада Рудольфа есть упрощенная версия здесь, что соответствует приведенному выше примеру реализации:

template <typename... T>
constexpr auto make_array(T&&... values) ->
    std::array<
       typename std::decay<
           typename std::common_type<T...>::type>::type,
       sizeof...(T)> {
    return std::array<
        typename std::decay<
            typename std::common_type<T...>::type>::type,
        sizeof...(T)>{std::forward<T>(values)...};
}
person Shafik Yaghmour    schedule 14.10.2014
comment
Да, но можете ли вы использовать это для хранения std::vector<std::array<type, different_sizes>>? Создание вектора std::array потребует, чтобы все размеры были одинаковыми? - person Brandon; 14.10.2014
comment
@Brandon К сожалению, я думаю, что они должны быть одинакового размера. Забудьте мое предложение в другой теме :( - person Neil Kirk; 14.10.2014
comment
@Брэндон std::vector<std::array<type, different_sizes> > Насколько я знаю, это невозможно. - person GingerPlusPlus; 14.10.2014
comment
Есть ли какая-либо причина, по которой вывод возвращаемого типа нельзя использовать в этом последнем примере или только return {std::forward<T>(values)...}? - person 0x499602D2; 14.10.2014
comment
Будет ли упрощенная версия make_array() @Konrad Rudolph выше, чем промежуточная версия (в обновленном исходном вопросе), опубликованная здесь? Каковы плюсы и минусы каждого? - person DarkMatter; 20.10.2014
comment
Последний пример очень хорош, за исключением того, что я не понимаю, почему он повторяет тип std::array<>, когда его объявление через auto -> позволяет нам просто return { the_initializer_list } не повторять имя типа. (Или это, безусловно, отлично компилируется для меня, как обычно.) - person underscore_d; 14.01.2016

Вы немного драматизируете, когда говорите, что «требуется такая ужасно сложная (для меня) функция». Вы можете сделать упрощенную версию самостоятельно, в предложение также входит функция "to_array" для преобразования C-массивов и вывода типа из первого параметра. Если вы оставите это, это становится вполне управляемым.

template<typename T, typename... N>
auto my_make_array(N&&... args) -> std::array<T,sizeof...(args)>
{
    return {std::forward<N>(args)...};
}

который вы можете затем назвать как

auto arr = my_make_array<int>(1,2,3,4,5);

редактировать: я должен упомянуть, что на самом деле в предложении есть версия, которую я пропустил, поэтому она должна быть более правильной, чем моя версия:

template <typename V, typename... T>
constexpr auto array_of(T&&... t)
    -> std::array < V, sizeof...(T) >
{
    return {{ std::forward<T>(t)... }};
}
person PeterT    schedule 14.10.2014
comment
Да, вау, 2-й пример отлично работает в большинстве случаев и совсем не сложен. Спасибо! - person underscore_d; 14.01.2016
comment
Единственная разница между двумя предложенными реализациями заключается в дополнительном наборе фигурных скобок? Какую синтаксическую разницу делает кажущаяся ненужной пара фигурных скобок? - person Violet Giraffe; 11.05.2018
comment
Чтобы добавить к этим комментариям (хотя и очень поздно), двойные фигурные скобки связаны с инициализацией std::array. Это описано здесь. Я думаю, что причина, по которой работает {std::forward<N>(args)...};, заключается в небольшом смягчении правил исключения фигурных скобок, описан здесь. - person dv_; 04.09.2018