2012-02-11 23 views
17

Una función llamada test toma std :: function <> como su parámetro.C++ 11 variad std :: function parámetro

template<typename R, typename ...A> 
void test(std::function<R(A...)> f) 
{ 
    // ... 
} 

Pero, si hago lo siguiente:

void foo(int n) { /* ... */ } 

// ... 

test(foo); 

compilador (gcc 4.6.1) dice no matching function for call to test(void (&)(int)).

Para hacer que la última línea test(foo) compile y funcione correctamente, ¿cómo puedo modificar la función test()? En la función test(), necesito f con el tipo de std :: función <>.

Es decir, ¿hay algún truco de plantilla para que el compilador determine la firma de la función (foo en el ejemplo), y la convierta a std::function<void(int)> automáticamente?

EDITAR

quiero hacer este trabajo para lambdas (ambos declarados y sin estado) también.

Respuesta

11

Parece que desea utilizar la sobrecarga

template<typename R, typename ...A> 
void test(R f(A...)) 
{ 
    test(std::function<R(A...)>(f)); 
} 

Esta sencilla aplicación aceptará la mayoría si no todas las funciones que tratará de pasar. Las funciones exóticas serán rechazadas (como void(int...)). Más trabajo te dará más genéricos.

+0

¿Qué hay de lambdas (ambos declararon y sin estado)? –

+4

@Daniel estás de suerte. O haga 'test' una plantilla que acepte cualquier cosa (' T'). 'std :: function' no rechazará los objetos de función incompatibles directamente de todos modos, por lo que el objetivo de limitar el tipo del parámetro de plantilla de función no parece ser demasiado útil para mí aquí. –

+1

He intentado con '(T)', pero, ¿cómo se puede hacer para 'std :: function '? Parece que puedo obtener 'R' usando' std :: result_of <> ', pero' A ... '? –

4

Por lo general, es desaconsejable aceptar std::function por valor a menos que esté en 'delimitación binaria' (por ejemplo, biblioteca dinámica, API 'opaca') ya que acaba de presenciar que causan estragos en la sobrecarga. Cuando una función toma de hecho un valor de std::function, a menudo es la carga de quien llama la construcción del objeto para evitar los problemas de sobrecarga (si la función está sobrecargada).

Como quiera que haya escrito una plantilla, es probable que no esté usando std::function (como tipo de parámetro) para conocer las ventajas de la supresión de tipo. Si lo que quieres hacer es inspeccionar funtores arbitrarios, entonces necesitas algunos rasgos para eso. P.ej. Boost.FunctionTypes tiene rasgos como result_type y parameter_types. A, ejemplo funcional mínimo:

#include <functional> 

#include <boost/function_types/result_type.hpp> 
#include <boost/function_types/parameter_types.hpp> 
#include <boost/function_types/function_type.hpp> 

template<typename Functor> 
void test(Functor functor) // accept arbitrary functor! 
{ 
    namespace ft = boost::function_types; 

    typedef typename ft::result_type<Functor>::type result_type; 
    typedef ft::parameter_types<Functor> parameter_types; 
    typedef typename boost::mpl::push_front< 
     parameter_types 
     , result_type 
    >::type sequence_type; 
    // sequence_type is now a Boost.MPL sequence in the style of 
    // mpl::vector<int, double, long> if the signature of the 
    // analyzed functor were int(double, long) 

    // We now build a function type out of the MPL sequence 
    typedef typename ft::function_type<sequence_type>::type function_type; 

    std::function<function_type> function = std::move(functor); 
} 

Como nota final, No recomiendo introspección funtores (es decir pinchar para su tipo de resultado y tipos de argumentos) en el caso general como que simplemente no funcionan para funtores polimórficas. Considere varios operator() sobrecargados: entonces no hay tipos de argumentos o tipos de resultados 'canónicos'. Con C++ 11 es mejor 'aceptar' ansiosamente cualquier tipo de functor, o restringirlos usando técnicas como SFINAE o static_assert según las necesidades, y más adelante (cuando los parámetros estén disponibles) usar std::result_of para inspeccionar el tipo de resultado para un conjunto dado de argumentos. Un caso en el que es conveniente limitar el frente es cuando el objetivo es almacenar funtores en, p. un contenedor de std::function<Sig>.

Para saber lo que quiero decir con el párrafo anterior, basta con probar el fragmento anterior con funtores polimórficos.

5

std::function implementa la interfaz de llamada, es decir, parece una función, pero eso no significa que deba exigir que los objetos invocables sean std::function s.

template< typename F > // accept any type 
void test(F const &f) { 
    typedef std::result_of< F(args) >::type R; // inspect with traits queries 
} 

Duck typing es la mejor política en metaprogramación de plantillas. Al aceptar un argumento de plantilla, sea inespecífico y simplemente deje que el cliente implemente la interfaz.

Si realmente necesita un std::function por ejemplo, para volver a apuntar la variable o algo así de loco, y usted sabe que la entrada es un puntero de función en bruto, se puede descomponer un tipo de puntero de función en bruto y reconsitute en una std::function.

template< typename R, typename ... A > 
void test(R (*f)(A ...)) { 
    std::function< R(A ...) > internal(f); 
} 

Ahora el usuario no puede pasar un std::function porque que se ha encapsulado dentro de la función. Podría mantener su código existente como otra sobrecarga y simplemente delegar a eso, pero tenga cuidado de mantener las interfaces simples.

En cuanto a Lambdas con estado, no sé cómo manejar ese caso. No se descomponen en indicadores de función y, por lo que yo sé, los tipos de argumentos no se pueden consultar ni deducir. Esta información es necesaria para instanciar std::function, para bien o para mal.

+1

Creo que el término correcto sería vinculante dinámico: el uso del término "tipado de pato" ha quedado obsoleto – serup

+0

@servidor Google => "Enlazado tardío o vinculación dinámica es un mecanismo de programación de computadora en el que se invoca el método sobre un objeto o la función que se llama con argumentos se busca por nombre en tiempo de ejecución ". No aplicable a plantillas. – Potatoswatter

+0

Entonces, ¿por qué usar el término pato escribiendo? – serup

2

Este es uno antiguo, y parece que no puedo encontrar mucho sobre el mismo tema, así que pensé en seguir adelante y escribir una nota.

Compilado en GCC 4.8.2, las siguientes obras:

template<typename R, typename... A> 
R test(const std::function<R(A...)>& func) 
{ 
    // ... 
} 

Sin embargo, no se puede simplemente llamar al pasar en sus punteros, lambdas, etc. Sin embargo, los ejemplos 2 dos trabajos siguientes con que:

test(std::function<void(int, float, std::string)>(
     [](int i, float f, std::string s) 
     { 
      std::cout << i << " " << f << " " << s << std::endl; 
     })); 

también:

void test2(int i, float f, std::string s) 
{ 
    std::cout << i << " " << f << " " << s << std::endl; 
} 

// In a function somewhere: 
test(std::function<void(int, float, std::string)>(&test2)); 

La desventaja de estos debe sobresalir bastante obvio: yo tienes que declarar explícitamente la función std :: para ellos, que puede parecer un poco fea.

Dicho eso, sin embargo, lo tiré junto con una tupla que se expande para llamar a la función entrante, y funciona, solo requiere un poco más de lo que está diciendo explícitamente llamando a la función de prueba.

Código de ejemplo que incluye la cosa tupla, si quiere jugar con él: http://ideone.com/33mqZA

+1

Solo por las patadas, se me ocurrió un método que utiliza index_sequence (se agrega en C++ 14, pero es fácil implementarlo en C++ 11) y una estructura de estilo de funciones funciona para encontrar algo que tomará cualquier lambda, functor o función, y lo usará. [http://ideone.com/LNpj74](http://ideone.com/LNpj74) muestra un ejemplo práctico de eso. Sin embargo, tenga en cuenta que para los funtores con 2+ operator() sobrecargados, se requiere una interfaz adicional que especifique los tipos que se usarán todavía. No lo intenté con funtores polimórficos, pero supongo que también causará problemas ... – user3694249

+0

Como Potatoswatter aludió anteriormente, cuando hay plantillas disponibles, usar 'std :: function' es una pila de sobrecarga de asignación dinámica y repetitivo sin ningún beneficio Es mejor simplemente crear una plantilla de función genérica que pueda aceptar una lambda, que tirar 'std :: function' por todo el lugar. –

Cuestiones relacionadas