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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.