Can someone help with how I can replace a list of template types with a variadic template parameter and create unique pointers of each type within the class.
The problem is I have a class
template <typename TA, typename TB1, typename TB2>
class Foo
which I want to extend to more than 2 TB
types as such
template <typename TA, typename ... TB>
class Foo
Full example code
#include <iostream>
#include <memory>
// Some concrete types
struct A
{
A(int a) : a_(2*a){}
void execute() const { std::cout << "A: " << a_ << "\n"; }
int a_;
};
struct B1
{
B1(int a, double b) : a_(a), b_(b){}
void execute() const { std::cout << "B1: " << a_ << ", " << b_ << "\n"; }
int a_; double b_;
};
struct B2
{
B2(int a, double b) : a_(a*a), b_(b*b){}
void execute() const { std::cout << "B2: " << a_ << ", " << b_ << "\n"; }
int a_; double b_;
};
// Now declare a templated class
template <typename TA, typename TB1, typename TB2>
class Foo
{
private:
// A unique pointer to each template type
std::unique_ptr<TA> m_a{};
std::unique_ptr<TB1> m_b1{};
std::unique_ptr<TB2> m_b2{};
public:
Foo() = default;
~Foo() = default;
void initialise(int a, double b)
{
m_a = std::make_unique<TA>(a);
m_b1 = std::make_unique<TB1>(a, b);
m_b2 = std::make_unique<TB2>(a, b);
}
void execute() const
{
m_a->execute();
m_b1->execute();
m_b2->execute();
}
};
int main(int, char**)
{
// Use templated class
Foo<A,B1,B2> foo;
foo.initialise(5, 3.14159);
foo.execute();
return 0;
}
Within the class Foo
I declare, instantiate and use unique pointers to each of the types TB1
and TB2
. With just 2 template parameters, I can easily do this. However, I don't know how to do this with a variadic template parameter.
Essentially I would like to change Foo
to look like this:
template <typename TA, typename ... TB>
class Foo
{
private:
// A unique pointer to each template type
std::unique_ptr<TA> m_a{};
// TODO: Declare list or tuple of unique pointers to each of the types in TB ...
public:
Foo() = default;
~Foo() = default;
void initialise(int a, double b)
{
m_a = std::make_unique<TA>(a);
// TODO: Instantiate the list/tuple of unique pointers
}
void execute() const
{
m_a->execute();
// TODO: Call execute on the list/tuple of unique pointers
}
};
The parts that I have labelled TODO are the ones I don't know (even if this can be done at all).
Some notes
TB
classes are distinct and do not necessarily have a common base classTB
classes do not have a default constructor.TB
classes have a non-default constructor with the same signature.Of course I'm also open to a completely different approach which allows me to have up to 6 TB classes without much code duplication.
Something along these lines, perhaps (not tested):
template <typename TA, typename ... TB>
class Foo
{
private:
std::unique_ptr<TA> m_a{};
std::tuple<std::unique_ptr<TB>...> m_bs;
public:
void initialise(int a, double b)
{
m_a = std::make_unique<TA>(a);
std::apply([&](std::unique_ptr<TB>&... p) {
(void(p = std::make_unique<TB>(a, b)), ...);
},
m_bs);
}
void execute() const
{
m_a->execute();
std::apply([&](std::unique_ptr<TB>&... p) {
(void(p->execute()), ...);
},
m_bs);
}
};