c++ templates templates-deduction

c++ - Deducción de la plantilla para la función basada en su tipo de devolución?



templates templates-deduction (6)

(Esta respuesta es la misma que @UncleBens, pero un poco más general ya que perfecciona cualquier argumento).

Esto es muy útil en lenguajes como haskell donde, por ejemplo, read tomará una cadena como entrada y la analizará de acuerdo con el tipo de devolución deseado.

(Aquí hay un código de muestra en ideone ).

Primero, comience con la función foo cuyo tipo de retorno queremos deducir:

template<typename Ret> Ret foo(const char *,int); template<> std::string foo<std::string>(const char *s,int) { return s; } template<> int foo<int >(const char *,int i) { return i; }

Cuando se le pida una cadena, devolverá la cadena que está en su primer argumento. Cuando se le pide un int, devolverá el segundo argumento.

Podemos definir una función auto_foo que se puede usar de la siguiente manera:

int main() { std::string s = auto_foo("hi",5); std::cout << s << std::endl; int i = auto_foo("hi",5); std::cout << i << std::endl; }

Para que esto funcione, necesitamos un objeto que almacene temporalmente los argumentos de la función, y también ejecute la función cuando se le solicite convert al tipo de devolución deseado:

#include<tuple> template<size_t num_args, typename ...T> class Foo; template<typename ...T> class Foo<2,T...> : public std::tuple<T&&...> { public: Foo(T&&... args) : std::tuple<T&&...>(std::forward<T>(args)...) {} template< typename Return > operator Return() { return foo<Return>(std::get<0>(*this), std::get<1>(*this)); } }; template<typename ...T> class Foo<3,T...> : std::tuple<T&&...> { public: Foo(T&&... args) : std::tuple<T&&...>(std::forward<T>(args)...) {} template< typename Return > operator Return() { return foo<Return>(std::get<0>(*this), std::get<1>(*this), std::get<2>(*this)); } }; template<typename ...T> auto auto_foo(T&&... args) // -> Foo<T&&...> // old, incorrect, code -> Foo< sizeof...(T), T&&...> // to count the arguments { return {std::forward<T>(args)...}; }

Además, lo anterior funciona para funciones de dos arg o tres arg, no es difícil ver cómo extender eso.

¡Esto es mucho código para escribir! Para cada función a la que aplicará esto, podría escribir una macro que haga esto por usted. Algo así en la parte superior de tu archivo:

REGISTER_FUNCTION_FOR_DEDUCED_RETURN_TYPE(foo); // declares // necessary structure and auto_???

y luego podrías usar auto_foo en tu programa.

Me gustaría poder usar la deducción de la plantilla para lograr lo siguiente:

GCPtr<A> ptr1 = GC::Allocate(); GCPtr<B> ptr2 = GC::Allocate();

en lugar de (lo que tengo actualmente):

GCPtr<A> ptr1 = GC::Allocate<A>(); GCPtr<B> ptr2 = GC::Allocate<B>();

Mi función actual Asignar se ve así:

class GC { public: template <typename T> static GCPtr<T> Allocate(); };

¿Sería esto posible eliminar los <A> y <B> adicionales?

Gracias


Del mismo modo que no puede sobrecargar funciones en el tipo de devolución, no puede hacer una deducción de plantilla en él. Y por la misma razón: si f () es una plantilla / sobrecarga que devuelve algo, qué tipo usar aquí:

f();


Eso no se puede hacer. El tipo de devolución no forma parte de la deducción de tipo, sino que es el resultado de haber coincidido con la firma de plantilla adecuada. Sin embargo, puede ocultarlo de la mayoría de los usos como:

// helper template <typename T> void Allocate( GCPtr<T>& p ) { p = GC::Allocate<T>(); } int main() { GCPtr<A> p = 0; Allocate(p); }

Si esa sintaxis en realidad es mejor o peor que el GCPtr<A> p = GC::Allocate<A>() inicial GCPtr<A> p = GC::Allocate<A>() es otra pregunta.

PS c ++ 11 le permitirá omitir una de las declaraciones de tipo:

auto p = GC::Allocate<A>(); // p is of type GCPtr<A>


Lo único que se me ocurre es: hacer que Allocate no sea una plantilla que devuelva un objeto proxy que no sea una plantilla que tenga un operador de conversión con plantilla que haga el trabajo real:

template <class T> struct GCPtr { }; class Allocator { public: template <class T> operator GCPtr<T>() { return GCPtr<T>(); } }; class GC { public: static Allocator Allocate() { return Allocator(); }//could give a call-back pointer? }; int main() { GCPtr<int> p = GC::Allocate(); }


Podría intentar usar una macro para eso. Aparte de eso, no veo cómo se supone que funciona con solo una declaración.

#define ALLOC(ptrname,type) GCPtr<type> ptrname = GC::Allocate<type>() ALLOC(ptr1,A);

Los puntos de Johannes son válidos. El problema >> se soluciona fácilmente. Pero creo que tener comas como parte del tipo requiere la extensión de varargs del preprocesador C99:

#define ALLOC(ptrname,...) GCPtr< __VA_ARGS__ > ptrname = GC::Allocate< __VA_ARGS__ >() ALLOC(ptr1,SomeTemplate<int,short>);


Podrías ir por la ruta opuesta.

Si está utilizando un compilador actualizado (MSVC 2010, que debería estar listo en un par de días, o la versión actual de GCC) y no le importa confiar en las características de C ++ 0x:

auto ptr1 = GC::Allocate<A>(); auto ptr2 = GC::Allocate<B>();

te ahorraría los <A> y <B> adicionales, pero no en el lado derecho. :)