Home > Enterprise >  Build initializer list for array by repeating n times
Build initializer list for array by repeating n times

Time:09-13

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

CodePudding user response:

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;
}

CodePudding user response:

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

  • Related