2012-05-25 15 views
6

Me gustaría escribir una plantilla que pueda deconstruir un tipo en una plantilla con parámetros de plantilla sin tipo junto con sus argumentos de plantilla sin tipo. Por ejemplo, deconstruiría Array<5> en template<int> Array y 5, pero funcionaría genéricamente para cualquier tipo de parámetro de plantilla sin tipo (tipos integrales, punteros, punteros de miembro, etc.).Resumiendo sobre tipos de parámetros de plantilla sin tipo

primer intento, con la especialización de plantilla:

template<typename T> struct foo { enum { n = 1 }; }; 

template<int x> struct bar { enum { n = x }; }; 

template<typename T, template<T> class X, T x> 
struct foo< X<x> > { enum { n = x }; }; // here x must be of integral type, but that's just for testing 

int main(int, char**) { return foo< bar<16> >::n; } 

Clang 3.1 dice:

test145.cpp:6:8: warning: class template partial specialization contains a template parameter that can not be deduced; this partial specialization will never be used 
struct foo< X<x> > { enum { n = x }; }; 
     ^~~~~~~~~~~ 
test145.cpp:5:19: note: non-deducible template parameter 'T'      
template<typename T, template<T> class X, T x> 
       ^
1 warning generated. 

segundo intento, con una plantilla de función:

template<typename T, T x> 
struct box 
{ 
    static constexpr T value() { return x; } 
}; 

template<typename T, template<T> class X, T x> 
box<T, x> foo(X<x>); 

template<int> struct asdf { }; 

int main(int, char**) { return decltype(foo(*(asdf<9>*)0))::value(); } 

Clang dice:

test150.cpp:12:41: error: no matching function for call to 'foo' 
int main(int, char**) { return decltype(foo(*(asdf<9>*)0))::value(); } 
             ^~~ 
test150.cpp:8:11: note: candidate template ignored: couldn't infer template argument 'T' 
box<T, x> foo(X<x>); 
     ^
1 error generated. 

GCC 4.7 dice cosas similares.

¿Es esto una limitación fundamental?

Pregunta adicional: Si es así, ¿hay alguna manera de manejar todas las infinitas posibilidades en una cantidad finita de código, incluso si es un código menos simple y genérico? (Se pone difícil con, por ejemplo, punteros: por la misma razón que parece que no puede escribir template<T> tampoco creo que pueda escribir template<T*>)

No pregunte por qué lo estoy preguntando.

+1

Debo decir: no entiendo exactamente lo que necesita –

+2

Creo que se podría resumir de la siguiente manera: Deje 'plantilla struct A {}'.¿Es posible (y si es así, cómo) escribir una plantilla 'arg' tal que' arg > :: template_' es 'template Array',' arg > :: type' es 'int', y' arg > :: value' es '5' (y de tipo' int'), y también lo hace tan genérico que cada posible argumento de plantilla sin tipo se puede manejar de esta manera. – reima

+0

Eso es básicamente eso. ¡Gracias por expresarlo más claro de lo que pude! – glaebhoerl

Respuesta

2

Esta otra pregunta se refiere básicamente lo mismo pero para la plantilla de tipo parámetros, en lugar de la plantilla no tipo parámetros: template metaprogramming: (trait for?) dissecting a specified template into types T<T2,T3 N,T4, ...>

Para tipo parámetros, lo que realmente es fácil. El código es el siguiente:

#include <tuple> 
#include <vector> 

template <class T> struct explode; 

template <template <class... Args> class T, class... N> 
struct explode<T<N...>> 
{ 
    typedef T<N...> type; 
    template <class... Args> using template_ = T<Args...>; 
    template <int I> using type_parameter = 
     typename std::tuple_element<I, std::tuple<N...>>::type; 
}; 

#if TESTING 
void test_harness() 
{ 
    typedef explode<std::vector<int>> exv; 

    exv::template_<char> vchar; // The second parameter still has its default argument! 
    exv::template_<exv::type_parameter<0>, exv::type_parameter<1>> vint; 

    static_assert(std::is_same<exv::template_<char>, std::vector<char>>::value, ""); 
    static_assert(std::is_same<decltype(vchar), std::vector<char>>::value, ""); 
    static_assert(std::is_same<decltype(vint), std::vector<int>>::value, ""); 
    static_assert(std::is_same<exv::type, std::vector<int>>::value, ""); 
    static_assert(std::is_same<exv::type_parameter<0>, int>::value, ""); 
    static_assert(std::is_same<exv::type_parameter<1>, std::allocator<int>>::value, ""); 
} 
#endif 

Pero para no de tipo parámetros, no he descubierto si es posible todavía. Puede comenzar con el código de aspecto similar

template <class... ArgTypes, template <ArgTypes... Args> class T, ArgTypes... N> 
struct explode<T<N...>> 
{ 
    typedef T<N...> type; 
    template <ArgTypes... Args> using template_ = T<Args...>; 
    template <int I> using type_of_parameter = 
     typename std::tuple_element<I, std::tuple<ArgTypes...>>::type; 
    template <int I> struct nontype_parameter { 
     static constexpr type_of_parameter<I> value() { 
      return std::get<I>(std::tuple<ArgTypes...>(N...)); 
     } 
    }; 
}; 

};

pero Sonido metálico (por lo menos) no lo acepta:

test.cc:8:8: warning: class template partial specialization contains a template 
     parameter that can not be deduced; this partial specialization will never 
     be used 
struct explode<T<N...>> 
     ^~~~~~~~~~~~~~~~ 
test.cc:7:20: note: non-deducible template parameter 'ArgTypes' 
template <class... ArgTypes, template <ArgTypes... Args> class T, ArgTypes... N> 
       ^

e incluso si ha realizado ese problema desaparezca alguna manera, todavía habría que reemplazar std::get con una versión codificada a mano constexpr, porque la biblioteca estándar std::get no es constexpr por ningún motivo.

+0

Sí, ese es básicamente el mismo problema con el que me topé con mi primer ejemplo. Si un parámetro de plantilla está presente solo en la lista de parámetros de la plantilla pero no en los argumentos de especialización, no se puede inferir. Es de suponer que el estándar exige esto, porque de lo contrario no parece algo que sería imposible. – glaebhoerl

Cuestiones relacionadas