2010-11-06 8 views
5

Il codice seguente non viene compilato su gcc 4.5 perché la chiamata a pippo è ambigua. Qual è il modo corretto per disambiguarlo?Disambiguating chiamate a funzioni che eseguono std :: functions

#include <iostream> 
#include <functional> 
using namespace std; 

void foo(std::function<void(int, int)> t) 
{ 
    t(1, 2); 
} 

void foo(std::function<void(int)> t) 
{ 
    t(2); 
} 

int main() 
{ 
    foo([](int a, int b){ cout << "a: " << a << " b: " << b << endl;}); 
} 

risposta

6

Il modo migliore è creare esplicitamente un oggetto del tipo corretto std::function poi passare tale oggetto alla funzione:

std::function<void(int, int)> func = 
    [](int a, int b) { cout << "a: " << a << " b: " << b << endl; } 
foo(func); 

o in linea:

foo(
    std::function<void(int, int)>(
     [](int a, int b) { cout << "a: " << a << "b: " << b << endl; } 
)); 

std::function ha un costruttore modello che accetta qualsiasi cosa:

template<class F> function(F); 

A causa di ciò, il compilatore non può sapere durante la risoluzione di sovraccarico che è possibile selezionare foo: sia std::function<void(int)> sia std::function<void(int, int)> hanno un costruttore che può assumere l'espressione lambda come argomento.

Quando si passa direttamente un oggetto std::function, il costruttore di copia std::function è preferito durante la risoluzione di sovraccarico, quindi viene selezionato al posto del modello di costruttore.


risposta per il futuro: Se l'elenco di cattura è garantito per essere vuota, è anche possibile utilizzare puntatori a funzione ordinarie. In C++ 0x, un lambda senza identificazione è implicitamente convertibile in un puntatore a funzione. Quindi, si può usare qualcosa di simile

void foo(void (*t)(int, int)) { t(1, 2); } 

void foo(void (*t)(int)) { t(1); } 

e chiamare foo direttamente con la lambda captureless (o un puntatore a funzione con il tipo di corrispondenza).

Si noti che questa conversione è un'aggiunta molto recente alla bozza dello standard di lingua (è stata aggiunta nel febbraio di quest'anno), quindi non è probabile che sia ampiamente supportata ancora. Visual C++ 2010 non lo supporta ancora; Non so l'ultimo g ++.

+0

Non esiste una funzione di fabbrica per 'std :: function', poiché l'inferenza di tipo è consentita sulle funzioni del modello ma non sulle classi di template? –

+0

@ Ben: Non credo che una funzione di fabbrica sia valida in questo caso. Si consideri un oggetto funzione che ha due overload 'operator()'; come dovrebbe essere selezionato il tipo 'std :: function'? –

+0

@James: oops, ho dimenticato che anche un lambda non catturante è un oggetto funzione. Esiste una funzione di fabbrica dai puntatori di funzione o l'ho completamente persa? –

3

Recentemente ho pensato a un problema simile e quando guardando intorno per eventuali soluzioni note mi sono imbattuto in questo post e la mancanza di soluzioni per la risoluzione

Una soluzione alternativa è quella di astratto su funtore come argomento di modello e usa decltype per risolvere il suo tipo. Quindi, l'esempio precedente diventerebbe:

#include <iostream> 
#include <functional> 
using namespace std; 

template<class F> 
auto foo(F t) -> decltype(t(1,2)) 
{ 
    t(1, 2); 
} 

template<class F> 
auto foo(F t) -> decltype(t(2)) 
{ 
    t(2); 
} 

int main() 
{ 
    foo([](int a, int b){ cout << "a: " << a << " b: " << b << endl;}); 
} 

Questo funziona come previsto con gcc 4.5.

Problemi correlati