Como posso construir um std :: array preenchido com algum valor uniforme?

Sep 09 2020

std::array pode ser construído (em tempo de compilação com versões mais recentes de C ++) com valores específicos, por exemplo

std::array a{1, 4, 9};

no entanto - ele não tem um construtor ou uma biblioteca padrão chamada constructor idiom, pegando um único valor e replicando-o. ou seja, não temos:

std::array<int, 3> a{11};
// a == std::array<int, 3>{11, 11, 11};

Como podemos, portanto, construir um array dado apenas o valor a ser repetido?

Edit: Estou procurando uma solução que funcione mesmo para tipos de elemento que não são construtíveis padrão; então, uma solução construindo o array padrão e, em seguida, preenchendo-o, não é o que eu procuro - apesar do fato de que funcionará no caso de int(como no exemplo).

Respostas

7 einpoklum Sep 09 2020 at 23:55

Podemos escrever um idioma de construtor nomeado apropriado para conseguir

A implementação é um pouco desajeitada, no entanto, já que precisamos usar o "truque dos índices" que exige muitos clichês em C ++ 11, então vamos supor que C ++ 14:

namespace detail {

template<size_t, class T>
constexpr T&& identity(T&& x) { return std::forward<T>(x); }

template<class T, size_t... Indices>
constexpr auto array_repeat_impl(T&& x, std::index_sequence<Indices...>)
{
    return std::experimental::make_array(identity<Indices>(x)...);
}

} // end detail

template<size_t N, class T>
constexpr auto array_repeat(T&& x)
{
    return detail::array_repeat_impl(std::forward<T>(x), std::make_index_sequence<N>());
}

Veja isso funcionando GodBolt.

Se você pode compilar seu código C ++ 20, pode abandonar a dependência make_arraye escrever:

namespace detail {

template<size_t, class T>
constexpr T&& identity(T&& x) { return std::forward<T>(x); }

template<class T, size_t... Indices>
constexpr auto array_repeat_impl(T&& x, std::index_sequence<Indices...>)
{
    return std::array{identity<Indices>(x)...};
}

} // end detail

template<size_t N, class T>
constexpr auto array_repeat(T&& x)
{
    return detail::array_repeat_impl(std::forward<T>(x), std::make_index_sequence<N>());
}

GodBolt

Notas:

  • Esta solução é um pouco semelhante à de Jared Hoberock tuple_repeat, parte de seus utilitários de tupla para C ++ 11 .
  • Os agradecimentos vão para @Caleth e @LF por apontar um encaminhamento inadequado no array_repeat_impl.
1 chris Sep 10 2020 at 00:26

Com C ++ 20, você pode fazer um auxiliar para isso diretamente quando o tipo for construtível por padrão e copiável usando a fillfunção now-constexpr ( exemplo ao vivo ):

#include <array>
#include <concepts>
#include <cstddef>

template<std::size_t N, std::semiregular T>
constexpr auto array_repeat(const T& value) -> std::array<T, N> {
    std::array<T, N> ret;
    ret.fill(value);
    return ret;
}

int main() {
    constexpr auto a = array_repeat<3>(11);
    static_assert(a == std::array<int, 3>{11, 11, 11});
}

Isso poderia ser menos eficiente, mas você decide se isso é um problema.