2012-08-20 18 views
9

Esta pregunta es un seguimiento de How to deduce the type of the functor's return value? Lo estoy reformulando de una manera más abstracta.¿Forma genérica de deducir el tipo de devolución de un funtor?

Dado el pseudocódigo de una función de plantilla

template <typename Arg, typename Fn> 
auto ComputeSomething(Arg arg, Fn fn) -> decltype(<decl-expr>) 
{ 
// do something 
// ............ 
return fn(<ret-expr>) 
} 

donde <ret-expr> es una expresión arbitraria que implica arg, ¿qué puedo utilizar para <decl-expr> para establecer el tipo de retorno de ComputeSomething igual al tipo de retorno de la funtor.

El functor puede ser una clase, un lambda o un puntero de función.

Soluciones parciales que encontré hasta ahora.

(a) La respuesta a mi pregunta vinculada realizada por ecatmur. Básicamente, está repitiendo la declaración de devolución en <decl-expr>. Problemas: es propenso a errores y no funcionaría si contiene variables locales.

(b) Sólo funciona para los punteros de función

template <typename Arg, typename Ret> 
Ret ComputeSomething(Arg arg, Ret(*fn)(Arg)) 

(c) Se supone que el argumento de la funtor es de tipo Arg (que no pueden sostener en general) y requiere Arg ser default- construible

template <typename Arg, typename Fn> 
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(Arg()) 

(d) Uso de std::declval que se supone que levantar la restricción por defecto Urbanizable, como se sugiere en how to deduce the return type of a function in template. ¿Alguien podría explicar cómo funciona?

template <typename Arg, typename Fn> 
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(std::declval<Arg>()) 
+0

Siento decir esto, pero yo sepa esto no es posible, porque el retorno de arrastre no ve la plantilla de función está definida, mientras que el el cuerpo lo hace. –

Respuesta

4

Aquí es mi propia solución, lo mejor que podría conseguir

template <typename Arg, typename Fn> 
typename std::result_of<Fn(Arg)>::type ComputeSomething(Arg arg, Fn fn) 
10

Use result_of. Es compatible con versiones anteriores y elimina todo el feo declval del código. Aún debe recordar agregar calificadores de referencia rvalue (&&) si solo reenvía los valores.

Algo más que considero importante: su función reenvía argumentos a otra función. En tales casos, siempre debe usar referencias rvalue para pasar los argumentos.

Si todo lo que intentas hacer es mejorar el mantenimiento: hay varios intentos en una macro RETURNS que intentan minimizar la repetición entre la declaración del tipo de devolución y la expresión de retorno real, pero no he visto ninguno que permita un cuerpo de función que contiene más que la declaración de retorno real.

En cuanto a cómo funciona declval: depende de su compilador. No está permitido que ocurra en un contenido evaluado y su argumento puede ser un tipo incompleto. Ver 20.2.4

+0

Recomendaría contra 'std :: result_of'. Por una vez no está garantizado SFINAE, a diferencia de las soluciones basadas en 'decltype'. Tampoco refleja el tipo de resultado de una llamada: hace más que eso, p. en relación con punteros a los miembros. –

+0

@LucDanton Siempre percibí "hacer más" como beneficio. Manejar el caso de punteros a funciones miembro es terriblemente tedioso y 'result_of' resuelve ese problema. ¿Puedes profundizar más en el tema SFINAE? – pmr

+0

No tiene sentido que 'std :: result_of' compute un tipo de resultado que la plantilla de función no puede tratar: 'f (a, b, c)' es un error de sintaxis cuando 'f' es un puntero al miembro. En cuanto a SFINAE, considere una 'aplicación' sobrecargada tal que' apply (f, args ...) 'resulta en' f (args ...) 'o, si no está bien formado, recurre a' f (* args ...) '(supongamos, en aras de la simplicidad, que esas dos expresiones nunca están bien formadas). Necesita SFINAE para eliminar cualquier sobrecarga para que una llamada tenga éxito. –

2

Una variante de (b) trabajar no sólo con los punteros de función deben ser algo así como

template<typename Arg, typename Ret> 
Ret ComputeSomething (Arg arg, function<auto (Arg) -> Ret> f) 
3

Para hacer (c) funciona para nada, necesita 2 sobrecargas. Primero como se muestra en (c), segundo:

template <typename Arg, typename Ret> 
Ret ComputeSomething(Arg arg, std::function<Ret(Arg)> fn) 

También, como gcc bug 54111 espectáculos - deducción del tipo de retorno es muy poco fiable.

11

std::declval es una plantilla de función que solo se declara (no está definida). Por lo tanto, solo se puede usar en contextos no evaluados, como el argumento sizeof y decltype. Se declara que devuelve un valor r del tipo especificado. Esto le permite usarlo para fabricar un parámetro ficticio para una llamada a función en una expresión decltype.

p. Ej.

typedef decltype(fn(std::declval<Arg>())) t; 

declara t ser el tipo del resultado de la llamada fn con un valor p de tipo Arg. Esto es similar a su caso (c) (fn(Arg())), pero no requiere nada de Arg, por lo que funciona en tipos sin constructores predeterminados.

Si su expresión de retorno usa una variable local del tipo foo, puede usar decltype(fn(std::declval<foo>())), nuevamente independientemente de cómo construya un foo.

Si necesita un lvalue, como un objeto con nombre o una referencia lvalue, puede usar std::declval<foo&>(). Esto le permite manejar el caso donde el tipo depende de si tiene un lvalue o un valor r.

+2

'std :: declval ()' también es algo que debe tenerse en cuenta, especialmente si se considera que se menciona una variable local. –

Cuestiones relacionadas