Why Does Std::Array Not Have an Constructor That Takes a Value for the Array to Be Filled With

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

How can I initialize an std::array of a class without a default constructor?

With copy constructor, something along these lines:

template <typename T, size_t... Is>
std::array<T, sizeof...(Is)> MakeArrayHelper(
const T& val, std::index_sequence<Is...>) {
return {(static_cast<void>(Is), val) ...};
}

template <typename T, size_t N>
std::array<T, N> MakeArray(const T& val) {
return MakeArrayHelper<T>(val, std::make_index_sequence<N>{});
}

std::array<Foo, 100> arr = MakeArray<Foo, 100>(Foo(5));

Actually, this can be done without copy constructor after all. This solution relies heavily on C++17's mandatory copy elision.

template <typename T, size_t... Is, typename... Args>
std::array<T, sizeof...(Is)> MakeArrayHelper(
std::index_sequence<Is...>, Args&&... args) {
return {(static_cast<void>(Is), T{std::forward<Args>(args)...}) ...};
}

template <typename T, size_t N, typename... Args>
std::array<T, N> MakeArray(Args&&... args) {
return MakeArrayHelper<T>(std::make_index_sequence<N>{},
std::forward<Args>(args)...);
}

Demo

Build initializer list for array by repeating n times

If you want to use a std::array you are going to need to build a helper function, namely a delegating constructor. Your default constructor will then call the delegate to actually initialize the member. That can look like

class MyClass {
public:
// default c'tor, create sequence of 10 integers
MyClass() : MyClass(std::make_index_sequence<10>{})
private:
// delegate, take a sequence and expand the initializer of myArray sizeof...(Is) times
template <std::size_t... Is>
MyClass(std::index_sequence<Is...>) :
myArray{ (Is, MyComplexType(func(const_arg1, const_arg2, const_arg3).method(const_arg4)))... } {}
std::array<MyComplexType, 10> myArray;
}

You can instead change myArray to be a vector instead and that would let you simplify the code to

class MyClass {
public:
MyClass() :
myData(MyComplexType(func(const_arg1, const_arg2, const_arg3).method(const_arg4)), 10) {}
private:
std::vector<MyComplexType> myData;
}

Declare static array in class with size passed to constructor?

Yes, this code

    class_name(int n, const int d)
{
float arr[d];
map = arr;
}

is a bad idea, for 2 reasons

  1. float arr[d]; creates a local variable in stack, so it ceases to exist at the end of the block. So map becomes a dangling pointer. If you needed dynamic size allocation, you should just use std::vector<float> map and avoid a lot of hassle.
  2. float arr[d]; is a variable length array, and C++ does not support those. Making d be const does not help, it has to be an actual constant, not const variable.

Solution: Since you say the array length can be determined at compile time, this is perfect fit for a template:

template <std::size_t N>
class class_name
{
public:
std::array<float, N> map { {} }; // { {} } causes value initialization of everything to 0
// actually above could be `float map[N];` but it has the C array gotchas

class_name(int n)
{
// not sure what n is for...
}
};

And to declare a variable of this class:

class_name<5> obj; // obj.map size is 5

How to initialize std::array member in constructor initialization list when the size of the array is a template parameter

std::index_sequence was provided in order to simplify the metaprogramming task of creating and expanding a pack whose size is not fixed. Use std::make_index_sequence and delegate the construction to some private constructor that deduces the pack:

A(H h) : A(h, std::make_index_sequence<N>{}) {}

template <std::size_t... i>
A(H h, std::index_sequence<i...>) : hashes{((void)i, h)...} {}

Here, ((void)i, h) is a comma expression that evaluates to h, but since we mentioned the pack i inside it, it's eligible to be expanded using .... The result is N copies of h (one for each element of i). This expansion occurs inside a braced-init-list, so the result is a braced-init-list contaning N copies of h, which will then initialize the hashes member.

Initialize an std::array of objects with non-trivial constructor

You might write helper class:

template <std::size_t ... Is, typename T>
std::array<T, sizeof...(Is)> make_array_impl(const T& def, std::index_sequence<Is...>)
{
return {{(Is, void(), def)...}};
}

template <std::size_t N, typename T>
std::array<T, N> make_array(const T& def)
{
return make_array_impl(def, std::make_index_sequence<N>());
}

and then

Cube(int size) : c_edgelets(make_array<12>(Edgelet(size - 2)) {}

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.



Related Topics



Leave a reply



Submit