6

std::array can be constructed (at compile time with newer C++ versions) with specific values, e.g.

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

however - it does not have a constructor, or a standard-library named constructor idiom, taking a single value and replicating it. i.e. we don't have:

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

How can we, therefore, construct an array given just the value to repeat?

Edit: I'm looking for a solution which would work even for element types which are not default constructible; so, a solution going through default-constructing the array, then filling it, is not what I'm after - despite the fact that this will work for the case of int (like in the example).

einpoklum
  • 102,731
  • 48
  • 279
  • 553

2 Answers2

7

We can write an appropriate named constructor idiom to achieve this

The implementation is a bit clunky, however, as we need to use the "indices trick" that takes a lot of boilerplate in C++11, so let's assume 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>());
}

See this working on GodBolt.

If you can compile your code C++20, you can drop the dependency on make_array and write:

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

Notes:

  • This solution is somewhat similar to Jared Hoberock's tuple_repeat, part of his tuple utilities for C++11.
  • Thanks goes to @Caleth and @L.F. for pointing out a inappropriate forwarding in array_repeat_impl.
einpoklum
  • 102,731
  • 48
  • 279
  • 553
  • Here I was thinking, "Matt has his own button now! W00t w00t!" But no. Just sneaky tagging. I commend the sneaky tagging. – user4581301 Sep 10 '20 at 00:09
  • @user4581301: But you don't like the answer? :-( – einpoklum Sep 10 '20 at 00:10
  • Needed to read the answer first and run it through my somewhat addled, grey and mushy main CPU. Probably because of how I think, I checked out the button code first, then got around to getting on topic.. – user4581301 Sep 10 '20 at 00:12
  • Not sure I like forwarding x multiple times in `array_repeat_impl` – Caleth Sep 10 '20 at 08:12
  • @Caleth: So, would you suggest something like `T&& x_ref = std::forward(x)` and using that instead? – einpoklum Sep 10 '20 at 08:36
  • No, just drop the forwarding at the point where you are copying x multiple times `std::array{identity(x)...};` – Caleth Sep 10 '20 at 08:37
  • To support Caleth's comment, currently `array_repeat<3>(std::string{"123"})` results in `["123", "", ""]`. – L. F. Sep 10 '20 at 09:27
  • @L.F.: You're right. The use of an rvalue reference messes up the value. Fixing it. – einpoklum Sep 10 '20 at 10:29
1

With C++20, you can make a helper for this straightforwardly when the type is default-constructible and copyable by using the now-constexpr fill function (live example):

#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});
}

This could potentially be less efficient, but whether that's a problem is up to you.

chris
  • 58,138
  • 13
  • 137
  • 194
  • Chris, I have to apologize, since I meant to ask about the case where you can't just default-construct-and-fill, and have to construct from the get-go with the repeated value. Perhaps I should also alter the example to reflect that. Again, my apology. – einpoklum Sep 10 '20 at 06:56
  • @einpoklum, It's fine, I suspected this wouldn't be useful to you specifically, but I hope someone will find it useful who didn't otherwise know this was a possibility. – chris Sep 10 '20 at 15:44