Variadic typedefs, или "Bimaps сделали С++ 0x путь"

Короткий вопрос: могу ли я ввести пакет вариативных аргументов? Мне нужно template <typename ...T> struct Forward { typedef T... args; };.


Длинная версия:

Я думал о переопределении отличного бинаумы повышения в С++ 0x. Напомним, что бимап двух типов S и T является std::set отношений между S x и T y. Сами объекты хранятся в двух независимых внутренних контейнерах, и отношения отслеживают связанные итераторы, я полагаю; оба типа могут служить в качестве ключей через "левый" и "правильный" поиск. В зависимости от выбора внутренних контейнеров значения могут быть уникальными или нет, например. если левый контейнер является множеством, а правый контейнер - мультимножеством, то один x может отображать множество разных y s, а правильный поиск дает равный диапазон. Популярные внутренние контейнеры: set, multiset, vector и list, а также версии unordered_*.

Итак, нам нужен тип, который принимает два контейнера в качестве параметров шаблона:

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

Но мы должны признать, что контейнеры могут принимать произвольные многие аргументы, поэтому мы должны передать все это. Если бы нам просто нужен один набор вариативных аргументов, это не было бы проблемой, поскольку мы могли бы передавать их напрямую. Но теперь нам нужны два набора аргументов, поэтому я хочу написать форвардер, который будет использоваться следующим образом:

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

Вот шаблон, который я придумал:

#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;
}

К сожалению, в указанной строке в Forward я не могу понять, как typedef пакет параметров! (Прокомментированная строка дает ошибку компилятора.)

[Полагаю, я мог бы пойти на ленивую версию Bimap<std::set<int, MyPred>, std::multiset<char, YourPred>> x; и извлечь типы через LeftCont::value_type и RightCont::value_type, но я подумал, что было бы лучше, если бы я мог сделать типы ключей моими основными аргументами шаблона и разрешить по умолчанию для контейнеров std::set.]

Ответ 1

Вы можете добиться того, чего хотите, инкапсулируя пакет вариационных аргументов в кортеж, а затем используя следующие две вспомогательные шаблонные структуры для пересылки фактических переменных:

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;
};

ваш пример кода будет выглядеть так:

#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;
}

который компилируется только под gcc 4.6.0

Ответ 2

Вы можете набрать кортеж. Тем не менее, я бы не знал, как затем вернуть типы обратно.

Проще всего было бы просто принять два полных типа.