2011-09-30 11 views
5

Estoy tratando de escribir una función de plantilla que acepte un std::function que depende de los argumentos de la plantilla. Lamentablemente, el compilador no es capaz de deducir correctamente los argumentos al std::function. Aquí algunos simples código de ejemplo:C++ 11 Función de plantilla que toma una función std :: que depende de los parámetros de la plantilla

#include <iostream> 
#include <functional> 

using namespace std; 

void DoSomething(unsigned ident, unsigned param) 
{ 
    cout << "DoSomething called, ident = " << ident << ", param = " << param << "\n"; 
} 

template < typename Ident, typename Param > 
void CallFunc(Ident ident, Param param, std::function< void (Ident, Param) > op) 
{ 
    op(ident, param); 
} 

int main() 
{ 
    unsigned id(1); 
    unsigned param(1); 

    // The following fails to compile 
    // CallFunc(id, param, DoSomething); 

    // this is ok 
    std::function< void (unsigned, unsigned) > func(DoSomething); 
    CallFunc(id, param, func); 

    return 0; 
} 

Si llamo al molde con el siguiente:

CallFunc(id, param, DoSomething); 

consigo los siguientes errores:

function-tpl.cpp:25: error: no matching function for call to CallFunc(unsigned int&, unsigned int&, void (&)(unsigned int, unsigned int))

Si creo explícitamente un std :: función del tipo correcto (o emitirlo) el problema desaparece:

std::function< void (unsigned, unsigned) > func(DoSomething); 
CallFunc(id, param, func); 

¿Cómo codificaría esto para que no se necesite el temporal explícito?

Respuesta

2

Si está utilizando las plantillas, puede evitar std::function por completo, a menos que por alguna razón desea restringir específicamente la función para tomar std::function:

template < typename Ident, typename Param, typename Func > 
void CallFunc(Ident ident, Param param, Func op) 
{ 
    op(ident, param); 
} 
+0

¿Olvidaste eliminar los caracteres de comentario "//" antes de la línea ofensiva? Tengo GCC4.6.1 y ese GCC rechaza dicha línea. –

+0

@litb * D'OH!* Bien descrito. –

0

Puede hacer la conversión en línea o usar bind. Tampoco es especialmente bonita, pero que se hagan el trabajo:

CallFunc(id, param, std::function<void(unsigned, unsigned)>(DoSomething)); 

CallFunc(id, param, std::bind(DoSomething, std::placeholders::_1, std::placeholders::_2)); 

+0

El std :: bind produce el siguiente error: function-tpl.cpp: 37: error: no hay función de coincidencia para llamar a 'CallFunc (unsigned int &, unsigned int &, std :: _ Bind , std :: _ Placeholder <2>)) (unsigned int, unsigned int)>) ' – mark

+0

@mark: Tiene razón, 'bind' no permite la deducción de argumentos, y tendría que decir' CallFunc < unsigned, unsigned> (...) '. Entonces ... solo usa la conversión explícita. –

8

Es necesario que el tercer parámetro de función sea un contexto no deducido para los parámetros de la plantilla en el mismo. Entonces el compilador no comparará el tipo de argumento con el tipo de parámetro sin considerar todas las conversiones implícitas (el Estándar dice, y C++ 0x clarificó esto aún más, que para un parámetro de función donde no hay parámetros de plantilla para deducir posiciones, todos implícitos se permiten conversiones para salvar una diferencia).

template < typename T > struct id { typedef T type; }; 

template < typename Ident, typename Param > 
void CallFunc(Ident ident, Param param, 
       typename id<std::function< void (Ident, Param) >>::type op) 
{ 
    op(ident, param); 
} 

En lugar de id puede utilizar boost::identity. En C++ 0x y compiladores que lo apoyan, puede tener una edición más legible el uso de plantillas de alias

template < typename T > using nondeduced = typename id<T>::type; 

A continuación, el código se convierte simplemente en

template < typename Ident, typename Param > 
void CallFunc(Ident ident, Param param, 
       std::function< nondeduced<void (Ident, Param)> > op) 
{ 
    op(ident, param); 
} 

Sin embargo GCC aún no soporta plantillas de alias.

+0

Ya habría pensado el parámetro en un contexto no deducido. ¡Esto es bueno saberlo! –

+0

¿Cómo es que la primera versión ajusta todo el tipo de 'función', pero la segunda versión solo ajusta el parámetro de la plantilla? –

+0

@KerrekSB debido a las diferentes interpretaciones en los compiladores de la redacción en C++ 03, llegué a ajustar todo el tipo de parámetro en un contexto no deducido. Algunos compiladores elegirían las partes de nivel superior del tipo antes de ingresar al contexto no deducido (es decir, las partes 'std :: function ') y comparar eso con los argumentos, lo que desencadenaría un error de deducción . Otros compiladores no harían eso. Si se envuelve alrededor de todo el tipo, no quedan partes "intactas", por lo que todo es '...' (agujero negro) y no pueden producirse desajustes. Ver http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1184 –

Cuestiones relacionadas