Инициализация std::array с постоянным значением

Мне нужно инициализировать все элементы std::array постоянным значением, как это можно сделать с std::vector.

#include <vector>
#include <array>

int main()
{
  std::vector<int> v(10, 7);    // OK
  std::array<int, 10> a(7);     // does not compile, pretty frustrating
}

Есть ли способ сделать это элегантно?

Прямо сейчас я использую это:

std::array<int, 10> a;
for (auto & v : a)
  v = 7;

но я бы хотел избежать использования явного кода для инициализации.


person Jabberwocky    schedule 02.09.2019    source источник


Ответы (5)


С std::index_sequence вы можете сделать:

namespace detail
{
    template <typename T, std::size_t ... Is>
    constexpr std::array<T, sizeof...(Is)>
    create_array(T value, std::index_sequence<Is...>)
    {
        // cast Is to void to remove the warning: unused value
        return {{(static_cast<void>(Is), value)...}};
    }
}

template <std::size_t N, typename T>
constexpr std::array<T, N> create_array(const T& value)
{
    return detail::create_array(value, std::make_index_sequence<N>());
}

С использованием

auto a = create_array<10 /*, int*/>(7); // auto is std::array<int, 10>

Которые, в отличие от решения std::fill, обрабатывают конструктивный тип не по умолчанию.

person Jarod42    schedule 02.09.2019
comment
Это самое элегантное решение для моих нужд. Очень похоже на не компилируемый std::array<int, 10> a(7), который становится auto a = create_array<10, int>(7) или даже auto a = create_array<10>(7) - person Jabberwocky; 02.09.2019
comment
Чем все это отличается от простого метода с foreach внутри? - person Michel Feinstein; 05.09.2019
comment
@mFeinstein: в отличие от решения std::fill, обрабатывать конструируемый тип не по умолчанию.. - person Jarod42; 05.09.2019
comment
Ваше значение foreach или for-range to set будет эквивалентно тому, что будет делать fill. - person Jarod42; 05.09.2019
comment
Понятно... ну, я не настолько опытен в C++, поэтому в этом ответе используется множество концепций, с которыми я не знаком - person Michel Feinstein; 05.09.2019

Увы, нет; std::array поддерживает агрегированную инициализацию, но здесь этого недостаточно.

К счастью, вы можете использовать std::fill или даже std::array<T,N>::fill, что из C++20 является элегантным, поскольку последнее становится constexpr.

Ссылка: https://en.cppreference.com/w/cpp/container/array/fill

person Bathsheba    schedule 02.09.2019

Вы можете сделать следующее

std::array<int, 10> a; 
a.fill(2/*or any other value*/);

Или используйте std::fill из заголовочного файла алгоритмов. Чтобы включить заголовочный файл алгоритмов, используйте

#include <algorithm>
person h4ckthepl4net    schedule 02.09.2019
comment
Спасибо, что нашли время написать фактический код. Это то, что я не делаю достаточно часто: переполнение стека работает лучше всего, когда есть несколько ответов на выбор. - person Bathsheba; 02.09.2019
comment
@Вирсавия, нет проблем, приятель) - person h4ckthepl4net; 02.09.2019
comment
@Bathsheba Я пишу это с телефона, поэтому я не видел, чтобы вы уже ответили на вопрос - person h4ckthepl4net; 02.09.2019
comment
Я рад - вам следует ответить на вопрос, если вы считаете, что вам есть что сказать, возможно, еще не сказанного. Я думаю, что этот ответ в некотором смысле более полезен, чем мой. - person Bathsheba; 02.09.2019

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

#include <array>

template<typename T, size_t N>
constexpr auto make_array(T value) -> std::array<T, N>
{
    std::array<T, N> a{};
    for (auto& x : a)
        x = value;
    return a;
}

int main()
{
    auto arr = make_array<int, 10>(7);
}
person M.M    schedule 04.09.2019
comment
Однако требуется конструируемый по умолчанию T. - person Jarod42; 05.09.2019

Тип std::array — это агрегат, который поддерживает инициализацию списка:

std::array<int, 10> a{2, 2, 2, 2, 2, 2, 2, 2, 2, 2};

Он также поддерживает агрегатную инициализацию:

std::array<int, 10> a = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2};

Это неудобно и подвержено ошибкам для длинных массивов, и вам лучше использовать для них решение, подобное Jarod42.

person Davislor    schedule 02.09.2019
comment
Это неудобно и чревато ошибками для любой длины массива. - person Jabberwocky; 03.09.2019
comment
@Jabberwocky Принятый ответ великолепен, и я проголосовал за него. Это также излишество для массива из трех элементов. Остальные не работают на constexpr std::array. Так что эта техника иногда уместна. - person Davislor; 03.09.2019