2010-04-10 20 views
26

Me gustaría ser capaz de utilizar la deducción plantilla para lograr lo siguiente:Deducción de la plantilla para función basada en su tipo de devolución?

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 Asignar actual se parece a esto:

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

¿Sería esto posible desprender el extra < a> B y <>?

Gracias

+0

fwiw Tenía algo similar pero con un tipo de devolución basado en los tipos de argumentos del constructor. Hice una función auxiliar de plantilla 'make_complex_template_obj (the, args)', así que puedo usar 'auto' al iniciar las variables de esa función. presumiblemente por la misma razón que en la respuesta aceptada, a esa plantilla no se le pudo dar el tipo de retorno 'auto'. Afortunadamente, pude evitar duplicar el nombre de tipo en el 'return' porque para entonces sabía qué tipo venía y convirtió un _initialiser-list_ desnudo apropiadamente. toda una aventura! –

Respuesta

28

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. Puede, sin embargo, esconderse 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); 
} 

Ya sea que la sintaxis es en realidad ni mejor ni peor que el inicial GCPtr<A> p = GC::Allocate<A>() es otra cuestión.

P.S. C++ 11 le permitirá omitir una de las declaraciones de tipo:

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

Puede intentar usar una macro para ello. 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); 

Johannes 'points are valid. El problema >> se soluciona fácilmente. Pero creo que tienen comas como parte del tipo requiere el preprocesador C99 varargs extensión:

#define ALLOC(ptrname,...) GCPtr<__VA_ARGS__> ptrname = GC::Allocate<__VA_ARGS__>() 

ALLOC(ptr1,SomeTemplate<int,short>); 
+1

Observe que esta macro falla si hace 'ALLOC (ptr1, A );' (hay dos problemas: no hay espacio después de 'type' (alias' '>>') y la coma hace dos macro argumentos fuera de 'A '). –

+0

¿Y qué te compraría eso? Todavía tendría que mencionar el tipo, y es menos seguro que la solución de David con una plantilla de función incorporada. -1 de mi parte – sbi

+0

Puede resolver ambos problemas diciendo 'ALLOC (ptr1, (A ));' y reescribiendo la macro para pasar un tipo de función a 'plantilla struct ty; plantilla struct ty {typedef Ty type; }; 'y diga' GCPtr :: type> ptrname' en su lugar (y lo mismo con 'typename' para usar dentro de las plantillas. C++ 0x y algunos compiladores actuales de C++ 03 también permiten' typename' fuera de las plantillas , aunque). –

1

De la misma manera no se puede sobrecargar las funciones del tipo de cambio, no se puede hacer la deducción de plantilla en él. Y por la misma razón - si f() es una plantilla/sobrecarga que devuelve algo, ¿qué tipo de uso aquí:

f(); 
+1

Bueno, ya he pensado en eso. Mi clase de recolector de basura utiliza el recuento de referencias, y la invocación de GC :: Allocate() tendrá inherentemente 0 referencias que se limpiarán de todos modos. Esto es por supuesto si el código compilado/ – Marlon

+2

Error del compilador, a menos que aparezca en un molde ('(int) f();') ...? – UncleBens

+0

@UncleBens: ¡buena idea! Sin embargo, el compilador C++ actualmente no funciona de esta manera. – Vlad

18

La única cosa que puedo pensar en: hacer que asignar una plantilla no que devuelve un no -template objeto proxy que tiene un operador de conversión de plantilla, que hace 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(); 
} 
+2

Parece excesivo, pero aún así, no lo hice Conozca este patrón. Usted me enseñó algo. Entonces +1. – paercebal

+0

De todos modos, a primera vista, supongo que podría evitar el GC :: Asignar() y escribir: 'GCPtr p = Allocator();', ¿no? – paercebal

+1

Como dice el comentario, el objeto Allocator podría almacenar datos adicionales que recibe a través del constructor, por lo que GC :: Allocate puede decidir qué datos necesita para la operación. - Eventualmente, el constructor de ' GCPtr 'podría hacer el trabajo en sí mismo (invocar' GC :: Asignar '). – UncleBens

7

Usted podría ir a la ruta opuesta.

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

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

le ahorraría el extra <A> y <B>, simplemente no está en el lado derecho. :)

4

(Esta respuesta es la misma que @UncleBens, pero un poco más general, ya que perfecto transmita ningún argumento.)

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

(Aquí es sample code on ideone.)

En primer lugar, comenzar con la función foo cuyo tipo de retorno deseamos 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 preguntó por una cadena, que va a 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 utilizar 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 va a almacenar temporalmente los argumentos de la función, y también ejecutar la función cuando se le pide que convert para el tipo de retorno deseada:

#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, los trabajos anteriores para dos o arg arg funciones de tres, 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 como esto en la parte superior de su archivo:

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

y entonces se podría utilizar auto_foo en su programa.

+0

Me parece bastante interesante, pero creo que te falta el parámetro de especialización en auto_foo: 'auto auto_foo (T && ... args) -> Foo ', porque de lo contrario, no seleccionará la especialización en mi humilde opinión. – daminetreg

+0

Tienes razón. Actualizaré el código aquí. Probé el código en mi computadora, pero obviamente no lo copié exactamente. ¡Gracias! –

+0

En cualquier caso, es una buena forma de implementar esto. Gracias por el ejemplo. – daminetreg

Cuestiones relacionadas