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

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


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

Я думал о том, чтобы реализовать превосходный бимап в C++0x. Напомним, что бимап двух типов S а также T это std::set отношений между S x а также T y, Сами объекты хранятся в двух независимых внутренних контейнерах, а отношения отслеживают связанные итераторы, я полагаю; оба типа могут служить ключами при поиске "влево" и "вправо". В зависимости от выбора внутренних контейнеров значения могут быть уникальными или нет, например, если левый контейнер является множеством, а правый контейнер является мультимножеством, то один x можно сопоставить с множеством разных yс, и правильный поиск дает равный диапазон. Популярные внутренние контейнеры 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 Я не могу понять, как набрать определение пакета параметров! (Закомментированная строка выдает ошибку компилятора.)

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

2 ответа

Решение

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

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

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

Самое простое, что можно сделать, это просто принять два полных типа.

Другие вопросы по тегам