0

I have a std:array something like this:

class MyClass {
private:
    std::array<MyComplexType, 10> myArray;
}

In the constructor, I need to do the following:

MyClass::MyClass() : myArray({
    MyComplexType(func(const_arg1, const_arg2, const_arg3).method(const_arg4)),
    ... repeated 8 more times...
    MyComplexType(func(const_arg1, const_arg2, const_arg3).method(const_arg4))
})

Now, I want the 10 to be a compile-time constant that I can modify without having to copy&paste more (or less) of those initializers. They are all the same, I just need a "repeat n times". Everything up to and including C++17 is fair game.

I am 99.9% certain this should be doable with some template magic, but so far I came up with nothing that worked. Any ideas?

(And if it is impossible with templates, maybe with macros? Though I would hate having to go that direction...)

ThE_-_BliZZarD
  • 722
  • 2
  • 12
  • 26
  • 3
    [`std::array::fill`](https://en.cppreference.com/w/cpp/container/array/fill)? – 康桓瑋 Sep 12 '22 at 13:15
  • [Why does std::array not have an constructor that takes a value for the array to be filled with?](https://stackoverflow.com/a/41259045/2684539) – Jarod42 Sep 12 '22 at 13:15
  • 2
    @康桓瑋: That assumes default constructible type though. – Jarod42 Sep 12 '22 at 13:16
  • This requires a helper `constexpr` function that's specialized for `std::index_sequence`, that's used to generate the array's initial values. I can also think of a way to use a delegating constructor. It's a lot of painful, tedious, template-fu, but that's the price of C++... – Sam Varshavchik Sep 12 '22 at 13:25
  • Do you need to use a `std::array` here? `std::vector` will do this for you like `my_vector(MyComplexType(func(const_arg1, const_arg2, const_arg3).method(const_arg4)), 10)` Yes, it will have a dynamic allocation (only one) but depending on `MyComplexType` you might never even notice that performance impact. It also makes the code simpler and there is a lot of an advantage to having simpler code. – NathanOliver Sep 12 '22 at 13:30
  • @康桓瑋 /Jarod42: Yes, it is not default constructible. – ThE_-_BliZZarD Sep 12 '22 at 13:48
  • @NathanOliver: I liked std::array as everything is fixed at compile-time, but I don't *need* need it. You might be right and std::vector is the way to go. – ThE_-_BliZZarD Sep 12 '22 at 13:48
  • @SamVarshavchik Could you make a rough sketch on how that would work? Cause I don't see it yet, unfortunately – ThE_-_BliZZarD Sep 12 '22 at 13:49
  • About the vector: `MyClass::MyClass() : myVector(MyComplexType(func(const_args)), 10) { }` – Aconcagua Sep 12 '22 at 13:56

2 Answers2

1

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;
}
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • @Aconcagua Woops, forgot to use `Is`. Code updated. – NathanOliver Sep 12 '22 at 14:28
  • Thanks for the pointer, I went with this solution first, realized I have three such arrays, would require three different template arguments, had a moment of clarity and went with the vector solution instead... – ThE_-_BliZZarD Sep 12 '22 at 16:18
  • @ThE_-_BliZZarD Glad to help. If you would rather hve an array over a vector you could make a helper function like `make_array` which would allow you to have one template that handles all of the different types of arrays. I could provide an example of that if you are interested. – NathanOliver Sep 12 '22 at 16:51
0

You could wrap the type into another struct/class which would provide an appropriate default constructor:

int f(int n)
{
    return n;
}

struct S
{
    S(int n) : n(n) { }
    int n;
};

class MyClass
{
    struct Wrapper
    {
        S s;
        Wrapper() : s(f(7)) {}
    };

    std::array<Wrapper, 10> myArray;
};

You're now delegating the initialisation to the wrapper.

You might yet invest some work makeing the need of explicit indirection (myArray[i].s) obsolete, e.g. wrapping the array itself into another struct/class, too, providing the same interface as a std::array (as far as you need at least…) but with its iterators and index operators dereferencing to the complex type instead of to the wrapper (i.e. your iterator wraps around an array's iterator with S& operator*() { return i->s; } S* operator->() { return &i->s; } and further operators like increment just delegating to the wrapped iterator).

Aconcagua
  • 24,880
  • 4
  • 34
  • 59