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