Come si specifica un puntatore a una funzione sovraccaricata?

Voglio passare una funzione sovraccaricata std::for_each() . Per esempio,

 class A { void f(char c); void f(int i); void scan(const std::string& s) { std::for_each(s.begin(), s.end(), f); } }; 

Mi aspetto che il compilatore risolva f() dal tipo iteratore. Apparentemente, (GCC 4.1.2) non lo fa. Quindi, come posso specificare quale f() voglio?

È ansible utilizzare static_cast<>() per specificare quale f utilizzare in base alla firma della funzione implicita dal tipo di puntatore della funzione:

 // Uses the void f(char c); overload std::for_each(s.begin(), s.end(), static_cast(&f)); // Uses the void f(int i); overload std::for_each(s.begin(), s.end(), static_cast(&f)); 

Oppure puoi anche farlo:

 // The compiler will figure out which f to use according to // the function pointer declaration. void (*fpc)(char) = &f; std::for_each(s.begin(), s.end(), fpc); // Uses the void f(char c); overload void (*fpi)(int) = &f; std::for_each(s.begin(), s.end(), fpi); // Uses the void f(int i); overload 

Se f è una funzione membro, quindi è necessario utilizzare mem_fun , o per il tuo caso, utilizzare la soluzione presentata in questo articolo del Dr. Dobb .

Lambdas in soccorso! (nota: richiesto C ++ 11)

 std::for_each(s.begin(), s.end(), [&](char a){ return f(a); }); 

O usando decltype per il parametro lambda:

 std::for_each(s.begin(), s.end(), [&](decltype(*s.begin()) a){ return f(a); }); 

Con lambda polimorfo (C ++ 14):

 std::for_each(s.begin(), s.end(), [&](auto a){ return f(a); }); 

O disambigui rimuovendo il sovraccarico (funziona solo per le funzioni libere):

 void f_c(char i) { return f(i); } void scan(const std::string& s) { std::for_each(s.begin(), s.end(), f_c); } 

Perché non funziona

Mi aspetto che il compilatore risolva f() dal tipo iteratore. Apparentemente, (gcc 4.1.2) non lo fa.

Sarebbe bello se fosse così! Tuttavia, for_each è un modello di funzione, dichiarato come:

 template  UnaryFunction for_each(InputIterator, InputIterator, UnaryFunction ); 

La deduzione del modello deve selezionare un tipo per UnaryFunction nel punto della chiamata. Ma f non ha un tipo specifico – è una funzione sovraccaricata, ci sono molti file ciascuno con tipi diversi. Non esiste un modo per for_each per aiutare il processo di deduzione del template affermando quale f vuole, quindi la deduzione del template semplicemente fallisce. Per fare in modo che la deduzione del modello abbia successo, devi fare più lavoro sul sito di chiamata.

Soluzione generica per il fissaggio

Saltando qui da qualche anno e C ++ 14 dopo. Piuttosto che usare un static_cast (che consentirebbe la deduzione dei template per riuscire a “aggiustare” quale f vogliamo usare, ma richiede di eseguire manualmente la risoluzione di sovraccarico per “correggere” quella corretta), vogliamo far funzionare il compilatore per noi . Vogliamo chiamare f su alcuni argomenti. Nel modo più generico ansible, questo è:

 [&](auto&&... args) -> decltype(auto) { return f(std::forward(args)...); } 

È molto da scrivere, ma questo tipo di problema si presenta con una frequenza fastidiosamente frequente, quindi possiamo semplicemente racchiuderlo in una macro (sospiro):

 #define AS_LAMBDA(func) [&](auto&&... args) -> decltype(func(std::forward(args)...)) { return func(std::forward(args)...); } 

e poi basta usarlo:

 void scan(const std::string& s) { std::for_each(s.begin(), s.end(), AS_LAMBDA(f)); } 

Questo farà esattamente quello che vorresti che facesse il compilatore: esegui la risoluzione di sovraccarico sul nome f stesso e fai la cosa giusta. Questo funzionerà indipendentemente dal fatto che f sia una funzione libera o una funzione membro.

Non rispondere alla tua domanda, ma sono l’unico che trova

 for ( int i = 0; i < s.size(); i++ ) { f( s[i] ); } 

sia più semplice che più breve rispetto for_each suggerita da in silico in questo caso?

Il problema qui sembra non essere la risoluzione di sovraccarico ma in realtà la deduzione dei parametri del template . Mentre l’ eccellente risposta di @In silico risolverà un ambiguo problema di overload in generale, sembra che la soluzione migliore quando si ha a che fare con std::for_each (o simile) sia di specificare esplicitamente i parametri del template :

 // Simplified to use free functions instead of class members. #include  #include  #include  void f( char c ) { std::cout << c << std::endl; } void f( int i ) { std::cout << i << std::endl; } void scan( std::string const& s ) { // The problem: // error C2914: 'std::for_each' : cannot deduce template argument as function argument is ambiguous // std::for_each( s.begin(), s.end(), f ); // Excellent solution from @In silico (see other answer): // Declare a pointer of the desired type; overload resolution occurs at time of assignment void (*fpc)(char) = f; std::for_each( s.begin(), s.end(), fpc ); void (*fpi)(int) = f; std::for_each( s.begin(), s.end(), fpi ); // Explicit specification (first attempt): // Specify template parameters to std::for_each std::for_each< std::string::const_iterator, void(*)(char) >( s.begin(), s.end(), f ); std::for_each< std::string::const_iterator, void(*)(int) >( s.begin(), s.end(), f ); // Explicit specification (improved): // Let the first template parameter be derived; specify only the function type std::for_each< decltype( s.begin() ), void(*)(char) >( s.begin(), s.end(), f ); std::for_each< decltype( s.begin() ), void(*)(int) >( s.begin(), s.end(), f ); } void main() { scan( "Test" ); } 

Se non ti dispiace usare C ++ 11, ecco un assistente intelligente che è simile a (ma meno brutto di) il cast statico:

 template auto resolve(R (T::*m)(Args...)) -> decltype(m) { return m; } template auto resolve(R (T::*m)(void)) -> decltype(m) { return m; } 

(Funziona per le funzioni dei membri, dovrebbe essere ovvio come modificarlo per funzionare per le funzioni indipendenti, e dovresti essere in grado di fornire entrambe le versioni e il compilatore selezionerà quello giusto per te.)

Grazie a Miro Knejp per avermi suggerito: vedi anche https://groups.google.com/a/isocpp.org/d/msg/std-discussion/rLVGeGUXsK0/IGj9dKmSyx4J .