2010-07-08 18 views
31

Ho un metodo come questoScrivere una funzione che accetta un'espressione lambda come argomento

template<typename T, typename U> 
map<T,U> mapMapValues(map<T,U> old, T (f)(T,U)) 
{ 
    map<T,U> new; 
    for(auto it = old.begin(); it != old.end(); ++it) 
    { 
     new[it->first] = f(it->first,it->second); 
    } 
    return new; 
} 

e l'idea è che si chiamerebbe in questo modo

BOOST_AUTO_TEST_CASE(MapMapValues_basic) 
{ 
    map<int,int> test; 
    test[1] = 1; 
    map<int,int> transformedMap = VlcFunctional::mapMapValues(test, 
     [&](int key, int value) -> int 
     { 
      return key + 1; 
     } 
    ); 
} 

Tuttavia ho l'errore : nessuna istanza del modello di funzione "VlcFunctional :: mapMapValues" corrisponde ai tipi di argomenti dell'elenco di argomenti: (std :: map, std :: allocator >>, __lambda1)

Qualche idea di cosa sto facendo male? Visual Studio 2008 e Intel C++ Compiler 11.1

+0

'new' è una parola chiave non si può avere una variabile chiamata' new' – Motti

+1

haha. Sì, ho visto bene, perché il codice non stava trovando gli argomenti del template corretti, questo non è mai stato compilato in modo tale che il compilatore non si è mai preoccupato di indicarmelo :) –

+0

Non sono sicuro che "-> int" sia necessario per la funzione lambda – serup

risposta

34

tuo la funzione è in attesa di un puntatore di funzione, non di una lambda.

In C++, ci sono, in generale, 3 tipi di "oggetti chiamabili".

  1. Puntatori di funzione.
  2. Oggetti funzione.
  3. Funzioni lambda.

Se si vuole essere in grado di utilizzare tutti questi in vostra interfaccia funzione, allora si potrebbe utilizzare std::function:

template<typename T, typename U> 
map<T,U> mapMapValues(map<T,U> old, std::function<T(T, U)> f) 
{ 
    ... 
} 

Questo permetterà la funzione da chiamare utilizzando uno qualsiasi dei tre tipi di oggetti richiamabili sopra. Tuttavia, il prezzo per questa convenienza è una piccola quantità di sovraccarico sulle invocazioni sulla funzione (di solito un controllo puntatore nullo, quindi una chiamata attraverso un puntatore a funzione). Ciò significa che la funzione non è quasi certamente in linea (tranne forse con l'avanzata WPO/LTO).

In alternativa, è possibile aggiungere un parametro di modello aggiuntivo per prendere un tipo arbitrario per il secondo parametro. Ciò sarà più efficiente, ma perderà la sicurezza del tipo sulla funzione utilizzata e potrebbe portare a un aumento di codice.

template<typename T, typename U, typename F> 
map<T,U> mapMapValues(map<T,U> old, F f) 
+0

Mi sono imbattuto nel secondo approccio mentre esaminavo la sorgente std :: transform, tuttavia non riesco ancora a ottenere il primo approccio a causa dell'errore: lo spazio dei nomi "std" non ha una "funzione" membro. C'è un incluso di cui ho bisogno? –

+0

Relativo al secondo approccio: ci sono utility da "Boost" per estrarre i tipi di ritorno e l'elenco dei parametri da una funzione, mi chiedo se potrebbero funzionare con un oggetto lambda o funzione e potrebbero essere utilizzati lì con gli asser statici all'interno del codice. –

+0

@matthieu, a cui si riferiscono anche le utilità. Avevo creduto che la funzione boost :: avrebbe dovuto ottenere principalmente lo stesso effetto di @peter suggerito con std :: function, ma aveva gli stessi problemi di usare un puntatore a funzione nella dichiarazione. –

12

Il tuo tipo di parametro di dichiarazione T (f)(T,U) è di tipo 'funzione libera di prendere una T e un U e restituendo un T'. Non puoi passarlo a lambda, a un oggetto funzione oa qualcosa tranne una funzione reale con quella firma.

Si potrebbe risolvere questo modificando il tipo di parametro da std::function<T(T,U)> in questo modo:

template<typename T, typename U> 
map<T,U> mapMapValues(map<T,U> old, std::function<T(T,U)>) 
{ 
} 

alternativa, è possibile dichiarare il tipo di funzione come argomento di modello come questo:

template<typename T, typename U, typename Fn> 
map<T,U> mapMapValues(map<T,U> old, Fn fn) 
{ 
    fn(...); 
} 
+0

Grazie Joe, ho segnato la risposta di Peter solo perché era un minuto più veloce di te. Grazie mille. –

5

espressioni lambda con la lista di cattura vuota dovrebbero decadere di funzionare puntatori, secondo n3052. Tuttavia sembra che questa funzione non sia implementata in VC++ e solo parzialmente in g ++, vedi il mio SO question.

+0

e per niente in Intel C++ 11.1 –

10

Vorrei contribuire con questo semplice ma intuitivo esempio. Mostra come passare "cose ​​chiamabili" (funzioni, oggetti funzione e lambda) a una funzione oa un oggetto.

// g++ -std=c++11 thisFile.cpp 

#include <iostream> 
#include <thread> 

using namespace std; 

// ----------------------------------------------------------------- 
class Box { 
public: 
    function<void(string)> theFunction; 
    bool funValid; 

    Box() : funValid (false) { } 

    void setFun (function<void(string)> f) { 
    theFunction = f; 
    funValid = true; 
    } 

    void callIt() { 
    if (! funValid) return; 
    theFunction (" hello from Box "); 
    } 
}; // class 

// ----------------------------------------------------------------- 
class FunClass { 
public: 
    string msg; 
    FunClass (string m) : msg (m) { } 
    void operator() (string s) { 
    cout << msg << s << endl; 
    } 
}; 

// ----------------------------------------------------------------- 
void f (string s) { 
    cout << s << endl; 
} //() 

// ----------------------------------------------------------------- 
void call_it (void (*pf) (string)) { 
    pf("call_it: hello"); 
} //() 

// ----------------------------------------------------------------- 
void call_it1 (function<void(string)> pf) { 
    pf("call_it1: hello"); 
} //() 

// ----------------------------------------------------------------- 
int main() { 

    int a = 1234; 

    FunClass fc (" christmas "); 

    f("hello"); 

    call_it (f); 

    call_it1 (f); 

    // conversion ERROR: call_it ([&] (string s) -> void { cout << s << a << endl; }); 

    call_it1 ([&] (string s) -> void { cout << s << a << endl; }); 

    Box ca; 

    ca.callIt(); 

    ca.setFun (f); 

    ca.callIt(); 

    ca.setFun ([&] (string s) -> void { cout << s << a << endl; }); 

    ca.callIt(); 

    ca.setFun (fc); 

    ca.callIt(); 

} //() 
+1

Grandi esempi, grazie! :) –

+0

I tipi di calcestruzzo non modello erano esattamente ciò che dovevo vedere –

0

Ecco alcuni esempi di come passare una funzione come parametro di

class YourClass 
{ 
void YourClass::callback(void(*fptr)(int p1, int p2)) 
{ 
    if(fptr != NULL) 
     fptr(p1, p2); 
} 
}; 

void dummyfunction(int p1, int p2) 
{ 
    cout << "inside dummyfunction " << endl; 
} 

YourClass yc; 

// using a dummyfunction as callback 
yc.callback(&dummyfunction); 

// using a lambda as callback 
yc.callback([&](int p1, int p2) { cout << "inside lambda callback function" << endl; }); 

// using a static member function 
yc.callback(&aClass::memberfunction); 
Problemi correlati