Typedefs variádicos, o "bimaps realizados en la forma c ++ 0x"
Pregunta
Pregunta corta: ¿Puedo escribir un paquete de argumentos variádicos? Necesito template <typename ...T> struct Forward { typedef T... args; };
.
Versión larga:
Estaba pensando en reimplementar lo excelente impulsar bimap En C ++ 0x. Recuerde que un bimap de dos tipos S
y T
es un std::set
de relaciones Entre S x
y T y
. Los objetos en sí se almacenan en dos contenedores internos independientes, y las relaciones rastrean a los iteradores asociados, supongo; Ambos tipos pueden servir como claves a través de la búsqueda "izquierda" y "derecha". Dependiendo de la elección de los contenedores internos, los valores pueden ser únicos o no, por ejemplo, si el contenedor izquierdo es un conjunto y el contenedor derecho es un Multiset, entonces uno x
puede mapear a muchos diferentes y
S, y la búsqueda derecha da un rango de igual rango. Los contenedores internos populares son set
, multiset
, vector
y list
, y tal vez el unordered_*
versiones también.
Por lo tanto, necesitamos un tipo que acepte dos contenedores como parámetros de plantilla:
class Bimap<S, T, std::set, std::multiset>
Pero debemos aceptar que los contenedores pueden tomar muchos argumentos arbitrarios, por lo que también debemos aprobar todos esos. Si solo necesitábamos una Conjunto de argumentos variádicos, no sería un problema, ya que podríamos pasarlos directamente. Pero ahora necesitamos dos conjuntos de argumentos, por lo que quiero escribir un reenvío, para ser utilizado así:
Bimap<int, int, std::set, std::set, Forward<std::less<int>, MyAllocator>, Forward<std::greater<int>, YourAllocator>> x;
Aquí está la plantilla que se me ocurrió:
#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;
}
Desafortunadamente, en la línea indicada en Forward
¡No puedo entender cómo escribir el paquete de parámetros! (La línea comentada da un error del compilador).
Supongo que podría ir por una versión perezosa Bimap<std::set<int, MyPred>, std::multiset<char, YourPred>> x;
y extraer los tipos a través de LeftCont::value_type
y RightCont::value_type
, pero pensé que sería mejor si pudiera hacer de los tipos clave de mis argumentos de plantilla principal y permitir el valor predeterminado a std::set
contenedores.
Solución
Puede lograr lo que desea encapsulando el paquete de argumentos variádicos en una tupla y luego usando las siguientes dos estructuras de plantilla de ayuda para reenviar los argumentos variádicos reales:
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;
};
y
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;
};
Su muestra de código se vería así:
#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;
}
que se compila bien bajo GCC 4.6.0
Otros consejos
Puedes escribir una tupla. Sin embargo, no sabría cómo volver a salir los tipos.
Lo más fácil de hacer sería aceptar dos tipos completos.