2015-01-03 30 views
10

Si consideri il seguente esempiosovraccarico Funzione funzione utilizzando lambda firma

void foo(const std::function<int()>& f) { 
    std::cout << f() << std::endl; 
} 

void foo(const std::function<int(int x)>& f) { 
std::cout << f(5) << std::endl; 
} 

int main() { 
    foo([](){return 3;}); 

    foo([](int x){return x;}); 
} 

Questo non si compila, perché la chiamata a foo si dice che sia ambiguo. Per quanto ho capito, questo è dovuto al fatto che la funzione lambda non è a priori un std::function ma deve essere castata ad esso e che esiste un costruttore std::function che accetta un argomento arbitrario.

Forse qualcuno può spiegarmi perché qualcuno dovrebbe creare un costruttore implicito che accetta un argomento arbitrario. Tuttavia, la mia domanda acuta è se esiste una soluzione alternativa, che consente di utilizzare la firma della funzione delle funzioni lambda per sovraccaricare una funzione foo. Ho provato i puntatori di funzione, ma ciò non ha funzionato perché non è possibile eseguire il cast delle funzioni lambda su un normale puntatore a funzione.

Qualsiasi aiuto è più che benvenuto.

+7

Questo è probabilmente un bug in MSVC. Funziona in Clang/GCC. –

+2

Oh Microsoft, sei così divertente –

+0

[Works for me] (http://ideone.com/XmAgLG). Quale compilatore stai usando? –

risposta

15

Il compilatore è corretto secondo C++ 11. In C++ 14, viene aggiunta una regola che dice che il modello di costruttore non deve partecipare alla risoluzione di sovraccarico a meno che il tipo dell'argomento non sia effettivamente richiamabile con i tipi di argomento di std::function. Pertanto, questo codice dovrebbe compilare in C++ 14, ma non in C++ 11. Consideralo come una svista in C++ 11.

Per il momento, è possibile aggirare il problema conversione esplicita:

foo(std::function<int()>([](){return 3;})); 
+0

Grazie per i chiarimenti. Seguendo i commenti l'ho provato usando GCC 4.8 (e '-std = C++ 11') e ha funzionato. In qualche modo divertente che la versione più recente di GCC quindi non usi C++ 11 rigorosamente corretto. – Haatschii

+1

@Haatschii È un difetto nello standard C++ 11; gli sviluppatori di compilatori/librerie di solito applicano le correzioni retroattivamente.(Inoltre, era comunque UB in C++ 11, quindi è ammesso il comportamento di C++ 14). –

4

http://coliru.stacked-crooked.com/a/26bd4c7e9b88bbd0

Un'alternativa all'utilizzo di std :: funzione è quella di utilizzare i modelli. I modelli evitano l'overhead di allocazione della memoria associato a std :: function. Il meccanismo di deduzione del tipo di modello dedurrà il tipo corretto di lambda passato in modo che il cast del sito di chiamata si allontani. Tuttavia, è ancora possibile disambiguare i sovraccarichi per il caso no-args vs args.

È possibile eseguire questa operazione utilizzando un trucco con tipi di ritorno finali che si comportano in modo simile a enable_if.

template<typename Callable> 
auto baz(Callable c) 
    -> decltype(c(5), void()) 
{ 
    std::cout << c(5) << std::endl; 
} 

È possibile che questo sovraccarico di baz sarà solo un sovraccarico del candidato valido quando il parametro di template Callable può essere chiamato con un argomento di 5.

È possibile inserire meccanismi più avanzati in cima a questo per renderlo più generale (ad esempio l'espansione del pacchetto variadic degli argomenti in callable) ma volevo mostrare il funzionamento del meccanismo di base.