Mauricio Mauricio - 2 months ago 11
C++ Question

Class with member that uses variadic template

I'm trying to understand how templates work, and I've come up with this problem. Now, I'm perfectly aware that it can be solved using polymorphism, but I'm curious to see if it can be solved by just using templates. The situation is as follows:

Suppose I have two types of queues, which are defined as follows:

#include <queue>
template <typename GenType, typename Comparator>
class Priority
{
public:
Priority()
{ }
~Priority()
{ }
void insert(GenType const& t)
{ mQueue.push(t); }
private:
std::priority_queue<GenType, std::vector<GenType>, Comparator> mQueue;
};

template<typename GenType>
class FIFO
{
public:
FIFO()
{ }
~FIFO()
{ }
void insert(GenType const& t)
{ mList.push_front(t); }
private:
std::deque<GenType> mList;
};


And now, I have a class that can use either
Queue
or
FIFO
(or any other type of queue) as shown here:

// I'm not sure that this is how it should be declared...
template <typename GenType,
template<typename, typename...> class List>
class User
{
public:
User()
{ }
~User()
{ }
void add(GenType const& t)
{ mList.insert(t); }
private:
// This line gives an error.
List mList;
};


As it stands, the marked line gives an error, which I understand since I haven't actually passed any template parameters to
List
. The thing is that I don't know how to solve this error.

To give some context, the use case for this was to be able to have the
User
class take a any type of queue and could be used like this:

User<int, FIFO> u1;
// Not sure if it is this way:
User<int, Priority, std::less<int>> u2;
// Or this way:
User<int, Priority, std::less> u2;


Any suggestions on how to solve this problem?

Answer

Don't do it that way.

Instead, let your User class template take the full type of the container it's going to use, and let the container indicate what type of values it takes:

template <typename Container>
class User
{
public:
    using value_type = typename Container::value_type; // you'll have to add this
                                                       // typedef to your containers

    User() = default;
    ~User() = default;

    void add(value_type const& t)
    {
        mList.insert(t);
    }

private:
    Container mList;
};

This way, I, as the user of your class template, can provide the right thing. If I want to use your priority queue, I can pass in the comparator I want directly:

User<Priority<int, std::less<>>> u;

Or not:

User<FIFO<int>> u2;

Or maybe I wrote my own container which isn't even a class template:

User<SpecialContainer> u3;

Yours works either way. Generic is good.