How to Initialize Std::Array<T, N> Elegantly If T Is Not Default Constructible

How to initialize std::array T, n elegantly if T is not default constructible?

Given N, you could generate a sequence-type calledseq<0,1,2,3,...N-1> using a generator called genseq_t<>, then do this:

template<typename T, int N>
void f(T value)
{
//genseq_t<N> is seq<0,1,...N-1>
std::array<T, N> items = repeat(value, genseq_t<N>{});
}

where repeat is defined as:

template<typename T, int...N>
auto repeat(T value, seq<N...>) -> std::array<T, sizeof...(N)>
{
//unpack N, repeating `value` sizeof...(N) times
//note that (X, value) evaluates to value
return {(N, value)...};
}

And the rest is defined as:

template<int ... N>
struct seq
{
using type = seq<N...>;

static const std::size_t size = sizeof ... (N);

template<int I>
struct push_back : seq<N..., I> {};
};

template<int N>
struct genseq : genseq<N-1>::type::template push_back<N-1> {};

template<>
struct genseq<0> : seq<> {};

template<int N>
using genseq_t = typename genseq<N>::type;

Online demo

Hope that helps.

Initializing an std::array of non-default-constructible elements?

The usual.

template<size_t...Is>
std::array<foo_t, sizeof...(Is)> make_foos(std::index_sequence<Is...>) {
return { ((void)Is, make_foo())... };
}

template<size_t N>
std::array<foo_t, N> make_foos() {
return make_foos(std::make_index_sequence<N>());
}

Initialize array of non default constructible objects of template argument length

In the constructor of Thing, you can create a parameter pack with N elements, and forward the construction to a variadic constructor. The variadic constructor initializes the array with regular parameter expansion.

template <std::size_t N>
struct Thing
{
NonDefaultConstructibleClass _elements[N];
Thing() : Thing{build_indexes<N>{}} { }
template <std::size_t... Indexes>
Thing(indexes<Indexes...>)
: _elements{(Indexes, 3)...}
{ }
};

indexes and build_indexes are simple helper classes. C++14 will most likely contain something similar (std::integer_sequence). In GCC you can use the following type aliases:

template <std::size_t ...Indexes>
using indexes = std::_Index_tuple<Indexes...>;

template <std::size_t N>
using build_indexes = typename std::_Build_index_tuple<N>::_type;

Why does std::array not have an constructor that takes a value for the array to be filled with?

std::array is, by design, an aggregate, so has no user-declared constructors.

As you say, you could use fill after default constructing. Since it's an aggregate, default construction won't zero the memory, but will leave it uninitialised (if the contained type is trivially initialisable).

Initializing a std::array with a constant value

With std::index_sequence, you might do:

namespace detail
{
template <typename T, std::size_t ... Is>
constexpr std::array<T, sizeof...(Is)>
create_array(T value, std::index_sequence<Is...>)
{
// cast Is to void to remove the warning: unused value
return {{(static_cast<void>(Is), value)...}};
}
}

template <std::size_t N, typename T>
constexpr std::array<T, N> create_array(const T& value)
{
return detail::create_array(value, std::make_index_sequence<N>());
}

With usage

auto a = create_array<10 /*, int*/>(7); // auto is std::array<int, 10>

Which, contrary to std::fill solution, handle non default constructible types.

Construct std::array by filling with one element

Ok. Template magic. As std::array is an aggregate type, it can be initialized using aggregate initialization:

std::array<T, 5> arr = { one, two, three, four, five };

The idea is one, ..., five are five copies of a constructed object of type T (list in your case), using the user input as parameter. So, lets play. Don't laugh or cry after reading this:

The idea is to take one object of type T, and replicate it five times:

 { T(param), .... }; // Five repetitions.

So, lets create a function which returns the array already initialized:

std::array<A, 5> arr = create_array_by_copy<5>(A(10));

That function will return an array<A, 5> with 5 copies of that temporary object.

For that, we will use an auxiliary struct which will create a parameter pack with a length of 5 (parametrized as s):

template<std::size_t s, class... voids_t>
struct sized_pack : public sized_pack<s - 1, voids_t..., void>
{};

template<class... voids_t>
struct sized_pack<0, voids_t...>
{};

This will create a parameter pack, called voids_t, which is just a list of s voids. And now, the core of the trick:

template<std::size_t s, class T, class... pack_t>
std::array<T, s>
create_array_by_copy_helper(sized_pack<0, pack_t...> const&,
T const& o)
{ return { (pack_t(), o)... }; }

template<std::size_t s, class T>
std::array<T, s> create_array_by_copy(T const& o)
{
return create_array_by_copy_helper<s>(sized_pack<s>(), o);
}

That's complicated. I know... as we have passed an object of type sized_pack<s> to the helper function, that temporary object will instantiate a hierarchy of sized_pack which last base class will be an object of type sized_pack<0, void, void, void, void, void>.

The function apply will receive that object as a reference to size_pack<0, pack_t...> (the last base class, note the first 0), so, pack_t will be our list of 5 voids.

Finally, we have:

 (pack_t(), o)

which is just the comma operator, so, it returns o. The idea is that we have inserted pack_t (the parameter pack), inside the "pattern", so, when applying ... to the expression, it will be replaced by comma-separated expressions where each pack_t appearance will be replaced by each element in the parameter pack in the same order, so:

  { (pack_t(), o)... }

is transformed to:

  { (void(), o), (void(), o), (void(), o), (void(), o), (void(), o) }

an initialization list!! Finally, each element is just a void expressions followed by the coma operator and only the second element of each pair will be returned by the comma operator. So, the evaluted expression will be:

  return { o, o, o, o, o }; // With its corresponding calls to `forward`.

Our desired initialization list!!

Coliru example:

http://coliru.stacked-crooked.com/a/d6c4ab6c9b203130

You only need to replaced the type T with your list class.

templated array of duplicate elements without default constructor

Following will solve your issue:

#if 1 // Not in C++11

template <std::size_t ...> struct index_sequence {};

template <std::size_t I, std::size_t ...Is>
struct make_index_sequence : make_index_sequence<I - 1, I - 1, Is...> {};

template <std::size_t ... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> {};

#endif

namespace detail
{
template <typename T, std::size_t ... Is>
constexpr std::array<T, sizeof...(Is)> create_array(T value, index_sequence<Is...>)
{
// cast Is to void to remove the warning: unused value
return {{(static_cast<void>(Is), value)...}};
}
}

template <std::size_t N, typename T>
constexpr std::array<T, N> create_array(const T& value)
{
return detail::create_array(value, make_index_sequence<N>());
}

So test it:

struct TypeWithoutDefault {
TypeWithoutDefault(int i) : m_i(i) { }
int m_i;
};

int main()
{
auto ar1 = create_array<10>(TypeWithoutDefault(42));
std::array<TypeWithoutDefault, 10> ar2 = create_array<10>(TypeWithoutDefault(42));

return 0;
}

Array of unknown length in constructor initializer list

I found the answer to your problem here. So, in your case this solution should be applied like that:

#include <utility>
#include <array>

#define CONSTANT 2

class Data {
public:
Data(int number){}
};

template<typename T, size_t...Ix, typename... Args>
std::array<T, sizeof...(Ix)> repeat(std::index_sequence<Ix...>, Args &&... args) {
return {{((void)Ix, T(args...))...}};
}

template<typename T, size_t N>
class initialized_array: public std::array<T, N> {
public:
template<typename... Args>
initialized_array(Args &&... args)
: std::array<T, N>(repeat<T>(std::make_index_sequence<N>(), std::forward<Args>(args)...)) {}
};

class DemoClass {
private:
initialized_array<Data, CONSTANT> _member;
public:
DemoClass():
_member(1234)
{
// stuff
}
};

Then your _member is statically allocated fixed size array. That approach is a bit complicated though, so maybe someone can provide a cleaner solution.



Related Topics



Leave a reply



Submit