Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I construct an std::array filled with some uniform value?

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

like image 268
einpoklum Avatar asked Sep 09 '20 23:09

einpoklum


People also ask

How do you initialize an array of STDS?

Initialization of std::array Like arrays, we initialize an std::array by simply assigning it values at the time of declaration. For example, we will initialize an integer type std::array named 'n' of length 5 as shown below; std::array<int, 5> n = {1, 2, 3, 4, 5};

Is std:: array static?

std::array is a container that encapsulates fixed size arrays. This container is an aggregate type with the same semantics as a struct holding a C-style array T[N] as its only non-static data member. Unlike a C-style array, it doesn't decay to T* automatically.

How to declare an array with size in C++?

In a C++ array declaration, the array size is specified after the variable name, not after the type name as in some other languages. The following example declares an array of 1000 doubles to be allocated on the stack. The number of elements must be supplied as an integer literal or else as a constant expression.


2 Answers

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.
like image 188
einpoklum Avatar answered Oct 10 '22 16:10

einpoklum


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.

like image 39
chris Avatar answered Oct 10 '22 16:10

chris