Search code examples
c++c++17iterable-unpackingstdtuple

Expand tuple to parameters pack in member initializer lists


I need to initialize a base class with arguments stored in a std::tuple. I have access to C++17 and managed to figure out that std::make_from_tuple may work but would require a copy constructor for the base class.

An example:

#include <tuple>

template<class Base>
class WithTupleConstructor : public Base // Sort of a Mixin class template
{
    public:

    // Creating a Base with arguments stored in tuple and then copying it
    template<class Tuple>
    WithTupleConstructor(const Tuple& base_args)
    : Base(std::make_from_tuple<Base>(base_args)) }
    { }
};

class WithCopyConstructor
{
    public:
    
    WithCopyConstructor(int a, int b)
    {};
    WithCopyConstructor(const WithCopyConstructor& other)
    {};
};

class WithoutCopyConstructor
{
    public:
    WithoutCopyConstructor(int a)
    {};
    WithoutCopyConstructor(const WithoutCopyConstructor& other) = delete;
};

int main()
{
  WithTupleConstructor<WithCopyConstructor> m1(std::make_tuple(1,2));

  // this do not compiles
  //WithTupleConstructor<WithoutCopyConstructor> m2(std::make_tuple(1));
}

std::make_index_sequence and std::get seem to demand an auxiliary function and cannot see how they could be used to solve this (as explained here tuple-to-parameter-pack).

Is there a way to expand the tuple in the initialize list without requiring the copy constructor?


Solution

  • I found a solution based in this answer, turns out that it is achievable using std::make_index_sequence and std::get.

    An auxiliary function that "unwraps" the tuple is required but it can be defined as a private constructor:

    #include <tuple>
    
    template<typename Tuple>
    using make_tuple_index_sequence = std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>;
    
    template<class Base>
    class WithTupleConstructor : public Base // Sort of a Mixin class template
    {
        public:
    
        // Passing tuple and index sequence to auxiliary constructor
        template<class Tuple>
        WithTupleConstructor(const Tuple& base_args)
        : WithTupleConstructor(base_args, make_tuple_index_sequence<Tuple>{})
        {}
    
        private:
    
        // Expanding tuple using std::get
        template<class Tuple, std::size_t ...tuple_n>
        WithTupleConstructor(const Tuple& base_args, std::index_sequence<tuple_n...> )
        : Base(std::get<tuple_n>(base_args)...)
        {}
    };
    
    class WithCopyConstructor
    {
        public:
        
        WithCopyConstructor(int a, int b)
        {}
        WithCopyConstructor(const WithCopyConstructor& other)
        {}
    };
    
    class WithoutCopyConstructor
    {
        public:
    
        WithoutCopyConstructor(int a)
        {}
        WithoutCopyConstructor(const WithoutCopyConstructor& other) = delete;
    };
    
    int main()
    {
        WithTupleConstructor<WithCopyConstructor> m1(std::make_tuple(1,2));
    
        WithTupleConstructor<WithoutCopyConstructor> m2(std::make_tuple(1));
    }
    

    This can be further extended to support rvalues and perfect forwarding by adding && and std::forward when receiving and passing the tuples.