2010-07-08 6 views
31

J'ai une méthode comme celui-ciEcrire une fonction qui accepte une expression lambda comme argument

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

et l'idée est que vous appelleriez comme ça

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

Cependant, je reçois l'erreur : aucune instance du modèle de fonction "VlcFunctional :: mapMapValues" ne correspond aux types d'arguments de la liste d'arguments: (std :: map, std :: allocator >>, __lambda1)

Une idée de ce que je fais mal? Visual Studio 2008 et le compilateur Intel C 11.1

+0

'new' est un mot clé que vous ne pouvez pas avoir une variable appelée' new' – Motti

+1

haha. Oui bien vu, parce que le code ne trouvait pas les arguments de template corrects qui n'ont jamais été compilés, donc le compilateur n'a jamais pris la peine de me le signaler :) –

+0

Je ne suis pas sûr que le "-> int" soit nécessaire pour la fonction lambda – serup

Répondre

34

Votre La fonction attend un pointeur de fonction, pas un lambda.

En C++, il existe en général 3 types d '"objets appelables".

  1. Pointeurs de fonction.
  2. Objets fonctionnels.
  3. Fonctions Lambda.

Si vous voulez être en mesure d'utiliser tous ces éléments dans l'interface de votre fonction, vous pouvez ensuite utiliser std::function:

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

Cela permettra à la fonction à appeler en utilisant l'un des trois types de objets appelables ci-dessus. Cependant, le prix pour cette commodité est une petite quantité d'overhead sur les invocations sur la fonction (généralement une vérification de pointeur nul, puis un appel à travers un pointeur de fonction). Cela signifie que la fonction n'est presque certainement pas en ligne (sauf peut-être avec WPO/LTO avancé).

Vous pouvez également ajouter un paramètre de modèle supplémentaire pour prendre un type arbitraire pour le deuxième paramètre. Ce sera plus efficace, mais vous perdrez la sécurité de type sur la fonction utilisée, et pourrait conduire à plus de ballonnement de code.

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

Je suis tombé sur la deuxième approche tout en parcourant la source std :: transform, mais je n'arrive toujours pas à obtenir la première approche à cause d'une erreur: l'espace de noms "std" n'a pas de membre "function". Y a-t-il un inclus dont j'ai besoin? –

+0

Relatif à la seconde approche: il y a des utilitaires de 'Boost' pour extraire les types de retour et la liste des paramètres d'une fonction, je me demande s'ils fonctionneraient avec un objet lambda ou fonction et pourraient être utilisés avec des assertions statiques dans le code. –

+0

@matthieu, quels services utilisez-vous? Je pensais que la fonction boost :: devrait obtenir principalement le même effet que @peter avec std :: function, mais elle avait les mêmes problèmes que l'utilisation d'un pointeur de fonction dans la déclaration. –

12

Votre déclaration de type de paramètre T (f)(T,U) est de type 'fonction libre de prendre une T et un U et retourner un T. Vous ne pouvez pas lui passer un lambda, un objet fonction, ou quoi que ce soit, sauf une fonction réelle avec cette signature.

Vous pouvez résoudre ce problème en changeant le type de paramètre à std::function<T(T,U)> comme ceci:

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

Alternativement, vous pouvez déclarer le type de fonction comme un argument de modèle comme celui-ci:

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

Merci Joe, j'ai marqué la réponse de Peter seulement parce qu'il était une minute plus vite que vous. Merci, merci. –

5

expressions Lambda avec la liste de capture vides doivent se désintégrer pointeurs de fonction, selon n3052. Cependant, il semble que cette fonctionnalité n'est pas implémentée dans VC++ et seulement partiellement dans g ++, voir mon SO question.

+0

et pas du tout dans Intel C++ 11.1 –

10

Je voudrais contribuer cet exemple simple mais explicite. Il montre comment transmettre des "choses appelables" (fonctions, objets fonction et lambdas) à une fonction ou à un objet.

// 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

Grands exemples, merci! :) –

+0

Les types de béton non-modèle étaient exactement ce que j'avais besoin de voir –

0

Voici quelques exemples de la façon de passer une fonction en tant que paramètre

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); 
Questions connexes