Search code examples
c++templatestuplesinitializationvariadic

Initializing variadic-template-constructed tuple of types with constructor requiring an argument


I apologize already having asked a similar question here when I should have modified the question I asked first.

I have the class Level which constructor requires an argument. Using variadic templates I create a type LevelType for a member of the class Tree which is a tuple of elements of type Level.

How can I avoid repeating the value data in the initialization list of Tree?

As a further constraint, it should be possible for objects of class Tree to be constructed at compile time, so as far as I know I need some kind of template metaprogramming-fu.

Thank you!

Here's a code snippet:

#include<utility>
#include<array>
#include<iostream>

class LevelData
{};

template <typename LevelType>
class Level
{            
    public:
    Level(LevelData & data_):
    data(data_)
    {};

    LevelData & data ;
};

template <std::size_t nLevels,typename LevelType>
class Tree
{            
    public:
    Tree(LevelData & data):
    level(data,data,data,data,data)
    {};

    LevelType level ;                                                                                                                                        
};

template <std::size_t nLevels,auto... Is>
auto make_tree_impl(std::index_sequence<Is...>)
    -> Tree<nLevels,std::tuple<Level<std::array<double, std::size_t{1} << Is>>...>>;

template <std::size_t nLevels>
using make_tree = decltype(make_tree_impl<nLevels>(std::make_index_sequence<nLevels>{}));

int main()
{
    const unsigned int nLevels = 5;
    LevelData data;
    auto tree = make_tree<nLevels>(data);
    std::cout << std::is_same<decltype(tree),
    Tree<nLevels,std::tuple<Level<std::array<double,1>>,
                            Level<std::array<double,2>>,
                            Level<std::array<double,4>>,
                            Level<std::array<double,8>>,
                            Level<std::array<double,16>>>>>::value << std::endl ;
    return 0;
}

Solution

  • You could make use of make_index_sequence again, only this time it's used to repeat data as many times as you have types in your tuple.

    Unfolding can be done by using (static_cast<void>(Is), data)... since the left part of the , operator will be discarded, leaving only data, sizeof...(Is) number of times.

    template <std::size_t nLevels, typename LevelType>
    class Tree {
    public:
        Tree(LevelData& data)
            : level{[&]<std::size_t... Is>(std::index_sequence<Is...>) {
                  return LevelType{(static_cast<void>(Is), data)...};
              }(std::make_index_sequence<std::tuple_size_v<LevelType>>())} {}
    
        LevelType level;
    };