9

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).

0

2 Answers 2

10

We can write an appropriate named constructor idiom to achieve this. Then, your example definition would look like:

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

Here's how you could implement array_repeat():

#include <array>
#include <type_traits>
#include <utility>

template<std::size_t N, class T>
constexpr auto array_repeat(T&& value)
{
    return [&]<std::size_t... Indices>(std::index_sequence<Indices...>) {
        return std::array<std::decay_t<T>, N> { (void(Indices), value)... };
    } (std::make_index_sequence<N>());
}

(See this working on GodBolt). The implementation uses the "indices trick". And - we assumed C++14 to avoid an implementation of std::make_index_sequence, which requires a bit of boilerplate. But - this can be achieved in C++11.

Notes:

  • Thanks goes to @Caleth and @L.F. for pointing out an inappropriate forwarding in an earlier revision and to @Deduplicator who suggested the simplification of the solution to what you now see here.
  • Remember that there can are no arrays of references in C++ - neither plain C-style arrays nor std::arrays - so that if you use repeat_array() with, say, an std::string& or std::string&&, you'll get an array of std::strings (containing copies of the string you passed).
  • ... for which reason, you may therefore prefer to have a signature taking a T const& t to better indicate that copying is not avoided.
Sign up to request clarification or add additional context in comments.

5 Comments

Here I was thinking, "Matt has his own button now! W00t w00t!" But no. Just sneaky tagging. I commend the sneaky tagging.
@Caleth: So, would you suggest something like T&& x_ref = std::forward<T>(x) and using that instead?
No, just drop the forwarding at the point where you are copying x multiple times std::array{identity<Indices>(x)...};
To support Caleth's comment, currently array_repeat<3>(std::string{"123"}) results in ["123", "", ""].
@L.F.: You're right. The use of an rvalue reference messes up the value. Fixing it.
2

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.

2 Comments

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, 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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.