2011-09-30 23 views
5

Sto provando a scrivere una funzione modello che accetta uno std::function che dipende dagli argomenti del modello. Sfortunatamente il compilatore non è in grado di deporre correttamente gli argomenti allo std::function. Ecco qualche semplice codice di esempio:C++ 11 Funzione modello che utilizza una funzione std :: che dipende dai parametri del modello

#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; 
} 

Se chiamo il modello con il seguente:

CallFunc(id, param, DoSomething); 

Ottengo i seguenti errori:

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

Se creo esplicitamente uno std :: funzione del tipo corretto (o lanciarlo) il problema va via:

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

Come dovrei codificare questo in modo che il temporaneo esplicito non sia necessario?

risposta

2

Se si sta utilizzando i modelli, è possibile evitare std::function del tutto, a meno che per qualche motivo si desidera limitare in modo specifico la funzione a std::function:

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

Ti sei dimenticato di rimuovere i caratteri del commento "//" prima della riga che causa l'infrazione? Ho GCC4.6.1 e GCC rifiuta la linea. –

+0

@litb * D'OH!* Ben individuato. –

0

È possibile effettuare la conversione in linea o utilizzare bind. Nessuno dei due è particolarmente bella, ma ottenere il lavoro fatto:

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

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

+0

std :: bind genera il seguente errore: function-tpl.cpp: 37: errore: nessuna funzione di corrispondenza per la chiamata a "CallFunc (int non firmato e int non firmato, std :: _ Bind , std :: _ Segnaposto <2>)) (int unsigned, unsigned int)>) ' – mark

+0

@mark: hai ragione, 'bind' non consente la deduzione degli argomenti, e dovresti dire' CallFunc < unsigned, unsigned> (...) '. Quindi ... usa solo la conversione esplicita. –

8

È necessario rendere il terzo parametro di funzione un contesto non dedotto per i parametri del modello al suo interno. Quindi il compilatore non confronterà il tipo di argomento con il tipo di parametro senza considerare tutte le conversioni implicite (lo standard dice, e C++ 0x lo ha chiarito ulteriormente, che per un parametro di funzione in cui non ci sono parametri del modello nella deduzione delle posizioni, tutto implicito le conversioni sono consentite per colmare una differenza).

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); 
} 

Invece di id è possibile utilizzare boost::identity. In C++ 0x e compilatori che lo supportano, è possibile avere una versione più leggibile utilizzando i modelli alias

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

Poi il codice diventa semplicemente

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

Tuttavia GCC non supporta ancora i modelli di alias.

+0

Avrei già pensato il parametro in un contesto non dedotto. Questo è buono a sapersi! –

+0

Come mai la prima versione racchiude l'intero tipo 'function', ma la seconda versione include solo il parametro template? –

+0

@KerrekSB a causa di diverse interpretazioni nei compilatori del testo in C++ 03, sono arrivato a racchiudere l'intero tipo di parametro in un contesto non dedotto. Alcuni compilatori selezionerebbero le parti di toplevel del tipo prima di entrare nel contesto non dedotto (cioè le parti 'std :: function ' e confrontarle con gli argomenti, innescando un errore di deduzione . Altri compilatori non lo farebbero. Se si avvolge l'intero tipo, non vengono lasciate parti "intoccate", quindi è tutto '...' (buco nero) e non si possono verificare disallineamenti. Vedi http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1184 –

Problemi correlati