Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 variadic template template parameters

Keeping the old question. See below for resolution. It is probably something simple, but still. I have the following C++11 code fragment:

#include <vector>

template <typename... Ts>
struct typelist
{
};

template <typename T>
struct EventContainer
{
    typedef T Type;
    /// TODO. Ring buffer
    std::vector<T> container;

    void push(const T& t)
    {
        EventContainer<T>::container.push_back(t);
    }

    virtual ~EventContainer()
    {
    }
};


template <template <typename...> class TL>
class EventStorage:
        public EventContainer<Ts>...
{

};

class Event1
{
};

class Event2
{
};

typedef typelist<Event1,Event2> Events12;

int main()
{
    EventStorage<Events12> ev;

    return 0;
}

How can I make EventStorage inherit EventContainer templeted with each of the types in the typelist. I could do it with Loki:: library, but I want to use C++11 with variadic templates. Thank you.

Resolution1: Fixing EventStorage template template issue. This will make EventStorage, multiple inherit all EventContainer templated with each type of Ts.

template <typename...>
class EventStorage
{
};

template <typename... Ts>
class EventStorage < typelist<Ts...> >:
        public EventContainer<Ts>...
{

};

Now I have compile time error, on the following main():

int main()
{
    EventStorage<Events12> ev;
    Event1 ev1;
    ev.push(ev1);

    return 0;
}

In function ‘int main()’:
error: request for member ‘push’ is ambiguous
error: candidates are: void EventContainer<T>::push(const T&) [with T = Event2]
error: void EventContainer<T>::push(const T&) [with T = Event1]

Why the compiler is confused? After all I push with specific type. GCC 4.6.1 here.

Resolution2: As @Matthieu M. suggested I can present a forwarding method int EventStorage, but at a cost of one extra functin call:

template <typename T>
void push(const T& t)
{
    EventContainer<T>::push(t);
}

According to Alexandrescu, the compiler will optimize this forward call as long as parameters are references. Now the question is officially closed :)

like image 593
Dragomir Ivanov Avatar asked Feb 15 '12 14:02

Dragomir Ivanov


2 Answers

Is there any reason for introducing the typelist in the first place ?

template <typename T> struct Template { void push(T) {} };

template <typename... Args>
class Storage: public Template<Args>...
{
public:
  // forwarding...
  template <typename T>
  void push(T t) {
    Template<T>& me = *this;
    me.push(t);
  }
};

int main() {
  Storage< int, char > storage;
}

This works and you can typedef the whole Storage<...> bit.

EDIT: Following on comments regarding the possibility to "combine" types.

There are two solutions:

template <typename...> struct CombineStorage;

template <typename... A, typename... B>
struct CombineStorage<Storage<A...>, Storage<B...>> {
  typedef Storage<A..., B...> type;
};

Or simply provide a typelist adapter:

template <typename... Args>
class Storage<typelist<Args...>>: public Storage<Args...> {};
like image 125
Matthieu M. Avatar answered Oct 22 '22 15:10

Matthieu M.


At the moment, you're never even passing a typelist instantiation to the EventStorage, just the typelist template. So currently, there is no type pack to expand.

However, you should be able to unpack the typelist with a specialization and work with type packs otherwise:

template <typename...> class EventStorage;

template <typename Head, typename... Tail> class EventStorage<Head, Tail...>
  : public EventContainer<Head>, EventStorage<Tail...>
{
  using EventContainer<Head>::push;
  using EventStorage<Tail...>::push;
};

// allows you to pass typelists for convenience
template <typename... TL> class EventStorage<typelist<TL...>>
  : public EventStorage<TL...>
{
  using EventStorage<TL...>::push;
};

The using declarations just pull all the push methods into the same overload set, which seems to work for me.

The alternative would be to add a template method (maybe just to the toplevel typelist specialization) which explicitly forwards to this->EventContainer<T>::push, but it would require an exact type match.

like image 22
Useless Avatar answered Oct 22 '22 14:10

Useless