Variadic typedefs, or “Bimaps done the C++0x way”

丶灬走出姿态 提交于 2019-12-18 23:00:50

问题


Short question: Can I typedef a variadic argument pack? I need template <typename ...T> struct Forward { typedef T... args; };.


Long version:

I was thinking about reimplementing the excellent boost bimap in C++0x. Recall that a bimap of two types S and T is a std::set of relations between S x and T y. The objects themselves are stored in two independent internal containers, and the relations track the associated iterators I suppose; both types can serve as keys via "left" and "right" lookup. Depending on the choice of internal containers, values may be unique or not, e.g. if the left container is a set and the right container is a multiset, then one x can map to many different ys, and right lookup gives an equal-range. Popular internal containers are set, multiset, vector and list, and maybe the unordered_* versions too.

So we need a type which accepts two containers as template parameters:

class Bimap<S, T, std::set, std::multiset>

But we must accept that the containers can take arbitrary many arguments, so we need to pass all those, too. If we just needed one set of variadic arguments, it wouldn't be a problem, since we could pass those directly. But now we need two sets of arguments, so I want to write a forwarder, to be used like so:

Bimap<int, int, std::set, std::set, Forward<std::less<int>, MyAllocator>, Forward<std::greater<int>, YourAllocator>> x;

Here's the template I came up with:

#include <set>
#include <cstdint>

template <typename ...Args>
struct Forward
{
  typedef Args... args; // Problem here!!
  static const std::size_t size = sizeof...(Args);
};

template <typename S, typename T,
          template <typename ...SArgs> class SCont,
          template <typename ...TArgs> class TCont,
          typename SForward = Forward<>, typename TForward = Forward<>>
class Bimap
{
  typedef SCont<S, typename SForward::args> left_type;
  typedef TCont<T, typename TForward::args> right_type;

  template <typename LeftIt, typename RightIt> struct Relation; // to be implemented

  typedef Relation<typename left_type::const_iterator, typename right_type::const_iterator> relation_type;

};


int main()
{
  Bimap<int, int, std::set, std::set, Forward<std::less<int>>, Forward<std::greater<int>>> x;
}

Unfortunately, in the indicated line in Forward I cannot figure out how to typedef the parameter pack! (The commented line gives a compiler error.)

[I suppose I could go for a lazy version Bimap<std::set<int, MyPred>, std::multiset<char, YourPred>> x; and extract the types via LeftCont::value_type and RightCont::value_type, but I thought it'd be nicer if I could make the key types my primary template arguments and allow defaulting to std::set containers.]


回答1:


You can achieve what you want by encapsulating the variadic argument pack in a tuple and later using the following two helper template structs to forward the actual variadic arguments:

template<typename PackR, typename PackL>
struct cat;

template<typename ...R, typename ...L>
struct cat<std::tuple<R...>, std::tuple<L...>>
{
        typedef std::tuple<R..., L...> type;
};

and

template<typename Pack, template<typename ...T> class Receiver>
struct Unpack;

template<typename ...Args, template<typename ...T> class Receiver>
struct Unpack<std::tuple<Args...>, Receiver>
{
        typedef Receiver<Args...> type;
};

your code sample would look like this:

#include <set>
#include <cstdint>
#include <tuple>

template<typename PackR, typename PackL>
struct Cat;

template<typename ...R, typename ...L>
struct Cat<std::tuple<R...>, std::tuple<L...>>
{
        typedef std::tuple<R..., L...> type;
};

template<typename Pack, template<typename ...T> class Receiver>
struct Unpack;

template<typename ...Args, template<typename ...T> class Receiver>
struct Unpack<std::tuple<Args...>, Receiver>
{
        typedef Receiver<Args...> type;
};

template<typename ...Args>
struct Forward
{    
        //typedef Args... args; // Problem here!!
        typedef std::tuple<Args...> args; // Workaround

        static const std::size_t size = sizeof...(Args);
};

template<typename S, typename T, 
        template<typename ...SArgs> class SCont, 
        template<typename ...TArgs> class TCont, 
        typename SForward = Forward<> ,
        typename TForward = Forward<>>
class Bimap
{
        //typedef SCont<S, typename SForward::args> left_type;
        //typedef TCont<T, typename TForward::args> right_type;
        typedef typename Unpack<typename Cat<std::tuple<S>, typename SForward::args>::type, SCont>::type left_type; //Workaround
        typedef typename Unpack<typename Cat<std::tuple<T>, typename TForward::args>::type, TCont>::type right_type; //Workaround

        template<typename LeftIt, typename RightIt> struct Relation; // to be implemented

        typedef Relation<typename left_type::const_iterator, typename right_type::const_iterator> relation_type;

};

int main()
{
    Bimap<int, int, std::set, std::set, Forward<std::less<int>> , Forward<std::greater<int>>> x;
}

which compiles just fine under gcc 4.6.0




回答2:


You can typedef a tuple. However, I wouldn't know how to then get the types back out again.

The easiest thing to do would be to just accept two full types.



来源:https://stackoverflow.com/questions/6370867/variadic-typedefs-or-bimaps-done-the-c0x-way

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!