Que sont les fonctionnalités C++ et leurs utilisations?

j'entends beaucoup parler des functors en C++. Quelqu'un peut-il me donner un aperçu de ce qu'ils sont et dans ce cas, ils seraient utiles?

723

15 réponses

Un foncteur est à peu près juste une classe qui définit l'opérateur(). Cela vous permet de créer des objets qui "ressemblent à" une fonction:

// this is a functor
struct add_x {
  add_x(int x) : x(x) {}
  int operator()(int y) const { return x + y; }

private:
  int x;
};

// Now you can use it like this:
add_x add42(42); // create an instance of the functor class
int i = add42(8); // and "call" it
assert(i == 50); // and it added 42 to its argument

std::vector<int> in; // assume this contains a bunch of values)
std::vector<int> out(in.size());
// Pass a functor to std::transform, which calls the functor on every element 
// in the input sequence, and stores the result to the output sequence
std::transform(in.begin(), in.end(), out.begin(), add_x(1)); 
assert(out[i] == in[i] + 1); // for all i

il y a quelques bonnes choses sur les functors. L'une est que contrairement aux fonctions régulières, elles peuvent contenir l'état. L'exemple ci-dessus crée une fonction qui ajoute 42 à tout ce que vous donnez. Mais cette valeur 42 n'est pas codée en dur, elle a été spécifiée comme argument de constructeur lorsque nous avons créé notre instance functor. Je pourrais créez un autre adder, qui a Ajouté 27, juste en appelant le constructeur avec une valeur différente. Cela les rend facilement personnalisables.

comme le montrent les dernières lignes, vous passez souvent des foncteurs comme arguments à d'autres fonctions telles que std::transform ou les autres algorithmes de bibliothèque standard. Vous pouvez faire la même chose avec un pointeur de fonction régulier sauf que, comme je l'ai dit plus haut, les foncteurs peuvent être "personnalisés" parce qu'ils contiennent l'état, ce qui les rend plus flexibles (si je voulais utiliser une fonction pointer, je devrais écrire une fonction qui ajoute exactement 1 à son argument. Le functor est général, et ajoute ce que vous avez initialisé avec), et ils sont aussi potentiellement plus efficaces. Dans l'exemple ci-dessus, le compilateur sait exactement quelle fonction std::transform devrait appeler. Il devrait s'appeler add_x::operator() . Cela signifie qu'il peut en ligne Cette fonction Appel. Et qui le rend tout aussi efficace que si j'avais manuellement appelé la fonction sur chaque valeur du vecteur.

si j'avais passé un pointeur de fonction à la place, le compilateur ne pouvait pas voir immédiatement à quelle fonction il pointe, donc à moins qu'il n'effectue quelques optimisations globales assez complexes, il devrait déréférencer le pointeur à l'exécution, et puis faire l'appel.

881
répondu jalf 2015-08-19 18:00:54

petit ajout. Vous pouvez utiliser boost::function , pour créer des foncteurs à partir de fonctions et de méthodes, comme ceci:

class Foo
{
public:
    void operator () (int i) { printf("Foo %d", i); }
};
void Bar(int i) { printf("Bar %d", i); }
Foo foo;
boost::function<void (int)> f(foo);//wrap functor
f(1);//prints "Foo 1"
boost::function<void (int)> b(&Bar);//wrap normal function
b(1);//prints "Bar 1"

et vous pouvez utiliser boost:: bind pour ajouter l'État à ce foncteur

boost::function<void ()> f1 = boost::bind(foo, 2);
f1();//no more argument, function argument stored in f1
//and this print "Foo 2" (:
//and normal function
boost::function<void ()> b1 = boost::bind(&Bar, 2);
b1();// print "Bar 2"

et le plus utile, avec boost:: bind et boost:: function Vous pouvez créer functor à partir de la méthode de classe, en fait il s'agit d'un délégué:

class SomeClass
{
    std::string state_;
public:
    SomeClass(const char* s) : state_(s) {}

    void method( std::string param )
    {
        std::cout << state_ << param << std::endl;
    }
};
SomeClass *inst = new SomeClass("Hi, i am ");
boost::function< void (std::string) > callback;
callback = boost::bind(&SomeClass::method, inst, _1);//create delegate
//_1 is a placeholder it holds plase for parameter
callback("useless");//prints "Hi, i am useless"

Vous pouvez créer une liste ou un vecteur de foncteurs

std::list< boost::function<void (EventArg e)> > events;
//add some events
....
//call them
std::for_each(
        events.begin(), events.end(), 
        boost::bind( boost::apply<void>(), _1, e));

il y a un problème avec tous ces trucs, les messages d'erreur du compilateur ne sont pas lisibles par les humains:)

113
répondu Lazin 2017-06-07 11:33:44

un foncteur est un objet qui agit comme une fonction. Fondamentalement, une classe qui définit operator() .

class MyFunctor
{
   public:
     int operator()(int x) { return x * 2;}
}

MyFunctor doubler;
int x = doubler(5);

le véritable avantage est qu'un foncteur peut maintenir l'état.

class Matcher
{
   int target;
   public:
     Matcher(int m) : target(m) {}
     bool operator()(int x) { return x == target;}
}

Matcher Is5(5);

if (Is5(n))    // same as if (n == 5)
{ ....}
76
répondu James Curran 2017-06-07 11:34:28

Nom "foncteur" a été traditionaly utilisé dans les "151910920 catégorie" théorie longtemps avant le C++ est apparu sur la scène. Cela n'a rien à voir avec le concept C++ de functor. Il est préférable d'utiliser le nom objet de fonction au lieu de ce que nous appelons "functor" en C++. C'est ainsi que d'autres langages de programmation appellent des constructions similaires.

utilisé à la place de la fonction simple:

Dispose:

  • la Fonction de l'objet peut avoir l'état
  • objet de fonction s'inscrit dans OOP (il se comporte comme tout autre objet).

Inconvénients:

  • rend le programme plus complexe.

utilisé à la place du pointeur de fonction:

Dispose:

  • L'objet de la fonction peut souvent être inlined

Inconvénients:

  • L'objet de la fonction ne peut pas être échangé avec un autre type d'objet de la fonction pendant l'exécution (du moins à moins qu'il ne s'étende à une classe de base, ce qui donne donc une certaine hauteur)

utilisé à la place de la fonction virtuelle:

Dispose:

  • la Fonction de l'objet (non virtuel) ne nécessite pas vtable et runtime dispatching, donc il est plus efficace dans la plupart des cas

Inconvénients:

  • L'objet de la fonction ne peut pas être échangé avec un autre type d'objet de la fonction pendant l'exécution (du moins à moins qu'il ne s'étende à une classe de base, ce qui donne donc une certaine hauteur)
38
répondu doc 2017-02-13 10:46:57

comme d'autres l'ont mentionné, un functor est un objet qui agit comme une fonction, c'est-à-dire qu'il surcharge la fonction opérateur d'appel.

Les Functeurs

sont couramment utilisés dans les algorithmes STL. Ils sont utiles parce qu'ils peuvent tenir l'état avant et entre les appels de fonction, comme une fermeture dans les langues fonctionnelles. Par exemple, vous pouvez définir une fonction MultiplyBy qui multiplie son argument par un montant spécifié:

class MultiplyBy {
private:
    int factor;

public:
    MultiplyBy(int x) : factor(x) {
    }

    int operator () (int other) const {
        return factor * other;
    }
};

alors vous pourriez passer un objet MultiplyBy pour un algorithme comme std:: transform:

int array[5] = {1, 2, 3, 4, 5};
std::transform(array, array + 5, array, MultiplyBy(3));
// Now, array is {3, 6, 9, 12, 15}

un autre avantage d'un functor par rapport à un pointeur vers une fonction est que l'appel peut être inlined dans plus de cas. Si vous avez passé un pointeur de fonction à transform , à moins que que appel a été inlined et le compilateur sait que vous passez toujours la même fonction à lui, il ne peut pas inline l'appel à travers le pointeur.

34
répondu Matthew Crumley 2015-09-08 12:03:00

Pour les débutants comme moi parmi nous: après un peu de recherche, j'ai compris ce que le code jalf.com posté avez.

un functor est un objet de classe ou de structure qui peut être" appelé " comme une fonction. Ceci est rendu possible par la surcharge du () operator . Le () operator (pas sûr de son nom) peut prendre n'importe quel nombre d'arguments. Les autres opérateurs n'en prennent que deux, c'est-à-dire que le + operator ne peut prendre que deux valeurs (une de chaque côté de l'opérateur) et vous renvoie n'importe quelle valeur. ont surchargé. Vous pouvez adapter n'importe quel nombre d'arguments à l'intérieur d'un () operator qui est ce qui lui donne sa souplesse.

pour créer un functor, vous devez d'abord créer votre classe. Ensuite, vous créez un constructeur de la classe avec un paramètre de votre choix du type et de nom. Ceci est suivi dans la même déclaration par une liste d'initialiseur (qui utilise un seul opérateur colon, quelque chose que je ne connaissais pas encore) qui construit les objets membres de classe avec les objets précédemment déclarés paramètre du constructeur. Puis le () operator est surchargé. Enfin vous déclarez les objets privés de la classe ou de la structure que vous avez créée.

mon code (j'ai trouvé les noms de variables DE Ralf confus)

class myFunctor
{ 
    public:
        /* myFunctor is the constructor. parameterVar is the parameter passed to
           the constructor. : is the initializer list operator. myObject is the
           private member object of the myFunctor class. parameterVar is passed
           to the () operator which takes it and adds it to myObject in the
           overloaded () operator function. */
        myFunctor (int parameterVar) : myObject( parameterVar ) {}

        /* the "operator" word is a keyword which indicates this function is an 
           overloaded operator function. The () following this just tells the
           compiler that () is the operator being overloaded. Following that is
           the parameter for the overloaded operator. This parameter is actually
           the argument "parameterVar" passed by the constructor we just wrote.
           The last part of this statement is the overloaded operators body
           which adds the parameter passed to the member object. */
        int operator() (int myArgument) { return myObject + myArgument; }

    private: 
        int myObject; //Our private member object.
}; 

Si tout cela est inexact ou tout simplement faux hésitez pas à me corriger!

28
répondu Johanne Irish 2015-09-22 21:26:14

un functor est une fonction d'ordre supérieur qui applique une fonction aux types paramétrisés(c'est-à-dire templated). Il s'agit d'une généralisation de la fonction d'ordre supérieur map . Par exemple, nous pourrions définir un functor pour std::vector comme ceci:

template<class F, class T, class U=decltype(std::declval<F>()(std::declval<T>()))>
std::vector<U> fmap(F f, const std::vector<T>& vec)
{
    std::vector<U> result;
    std::transform(vec.begin(), vec.end(), std::back_inserter(result), f);
    return result;
}

cette fonction prend un std::vector<T> et renvoie std::vector<U> lorsqu'on lui donne une fonction F qui prend un T et renvoie un U . Un functor ne doit pas être défini sur les types de conteneurs, il peut être défini pour n'importe quel type templated aussi bien, y compris std::shared_ptr :

template<class F, class T, class U=decltype(std::declval<F>()(std::declval<T>()))>
std::shared_ptr<U> fmap(F f, const std::shared_ptr<T>& p)
{
    if (p == nullptr) return nullptr;
    else return std::shared_ptr<U>(new U(f(*p)));
}

est un exemple simple qui convertit le type en double :

double to_double(int x)
{
    return x;
}

std::shared_ptr<int> i(new int(3));
std::shared_ptr<double> d = fmap(to_double, i);

std::vector<int> is = { 1, 2, 3 };
std::vector<double> ds = fmap(to_double, is);

il y a deux lois que les fonctionnaires doivent suivre. La première est la Loi sur l'identité, qui stipule que si l'on attribue une fonction d'identité au foncteur, celle-ci doit être identique à l'application de la fonction d'identité à la fonction d'identité. type, c'est-à-dire fmap(identity, x) devrait être le même que identity(x) :

struct identity_f
{
    template<class T>
    T operator()(T x) const
    {
        return x;
    }
};
identity_f identity = {};

std::vector<int> is = { 1, 2, 3 };
// These two statements should be equivalent.
// is1 should equal is2
std::vector<int> is1 = fmap(identity, is);
std::vector<int> is2 = identity(is);

la loi suivante est la loi de composition, qui stipule que si le functeur est donné une composition de deux fonctions, il doit être le même que l'application du functeur pour la première fonction, puis à nouveau pour la deuxième fonction. Ainsi, fmap(std::bind(f, std::bind(g, _1)), x) devrait être le même que fmap(f, fmap(g, x)) :

double to_double(int x)
{
    return x;
}

struct foo
{
    double x;
};

foo to_foo(double x)
{
    foo r;
    r.x = x;
    return r;
}

std::vector<int> is = { 1, 2, 3 };
// These two statements should be equivalent.
// is1 should equal is2
std::vector<foo> is1 = fmap(std::bind(to_foo, std::bind(to_double, _1)), is);
std::vector<foo> is2 = fmap(to_foo, fmap(to_double, is));
17
répondu Paul Fultz II 2017-02-12 17:12:35

Voici une situation réelle où j'ai été forcé d'utiliser un Foncteur pour résoudre mon problème:

j'ai un ensemble de fonctions (disons, 20 d'entre elles), et elles sont toutes identiques, sauf chacune appelle une fonction spécifique différente en 3 points spécifiques.

c'est un gaspillage incroyable, et la duplication de code. Normalement, je passerais juste dans un pointeur de fonction, et j'appellerai ça dans les 3 spots. (Le code ne doit donc apparaître qu'une seule fois, au lieu de vingt.)

mais ensuite j'ai réalisé, dans chaque cas, que la fonction spécifique nécessitait un profil de paramètre complètement différent! Parfois 2 paramètres, parfois 5 paramètres,etc.

une Autre solution serait d'avoir une classe de base, où la fonction spécifique est une méthode redéfinie dans une classe dérivée. Mais est-ce que je veux vraiment construire tout cet héritage, juste pour pouvoir passer un indicateur de fonction????

SOLUTION: alors ce que j'ai fait était, j'ai fait classe wrapper (un "Foncteur") qui est capable d'appeler des fonctions dont j'ai besoin appelée. Je l'ai configuré à l'avance (avec ses paramètres, etc) et ensuite je le passe au lieu d'un pointeur de fonction. Maintenant, le code peut déclencher le Foncteur, sans savoir ce qui se passe à l'intérieur. Il peut même l'appeler plusieurs fois (j'ai besoin d'appeler 3 fois.)


Que c'est-un exemple pratique où un Foncteur s'est avéré être l'évident et facile solution, qui m'a permis de réduire la duplication de code de 20 fonctions à 1.

8
répondu Fellow Traveler 2011-12-26 06:54:56

sauf pour utilisé dans le callback, c++ functors peut également aider à fournir un Matlab style d'accès de goût à un matrice . Il y a un exemple .

2
répondu Shawn Xie 2012-03-07 07:03:17

Foncteurs sont utilisés dans z gtkmm pour connecter certains GUI bouton à une fonction C++ ou de la méthode.


si vous utilisez la bibliothèque pthread pour rendre votre application multithreaded, les foncteurs peuvent vous aider.

Pour lancer un thread, l'un des arguments du pthread_create(..) est le pointeur de fonction à exécuter sur son propre thread.

Mais il y a un inconvénient. Ce pointeur peut pas être un pointeur vers un méthode , à moins que ce soit une méthode statique , ou à moins que vous spécifiez que c'est la classe , comme class::method . Et autre chose, l'interface de votre méthode ne peut être que:

void* method(void* something)

donc vous ne pouvez pas exécuter (d'une manière évidente simple), les méthodes de votre classe dans un thread sans faire quelque chose de plus.

une très bonne façon de traiter les threads en C++, est de créer votre propre classe Thread . Si vous voulais pour exécuter des méthodes de la classe MyClass , ce que j'ai fait était, transformer ces méthodes en Functor classes dérivées.

aussi, la classe Thread a cette méthode: static void* startThread(void* arg)

Un pointeur vers cette méthode sera utilisé comme argument pour appeler pthread_create(..) . Et ce que startThread(..) devrait recevoir en arg est une référence void* casted à une instance en tas de n'importe quelle classe dérivée Functor , qui sera casté en arrière à Functor* lorsqu'il est exécuté, puis appelé "méthode 1519130920".

1
répondu erandros 2011-10-24 18:57:24

pour ajouter, j'ai utilisé des objets de fonction pour adapter une méthode d'héritage existante au modèle de commande; (seulement endroit où la beauté du paradigme OO vrai OCP j'ai senti ); En ajoutant également ici le modèle d'adaptateur de fonction connexe.

supposons que votre méthode ait la signature:

int CTask::ThreeParameterTask(int par1, int par2, int par3)

nous allons voir comment nous pouvons l'adapter pour le modèle de commande - pour cela, d'abord, vous devez écrire un adaptateur de fonction de membre afin qu'il puisse être appelé comme une fonction objet.

Note-c'est laid, et peut-être vous pouvez utiliser les helpers Boost bind, etc. mais si tu ne peux pas ou ne veux pas, c'est un moyen.

// a template class for converting a member function of the type int        function(int,int,int)
//to be called as a function object
template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
class mem_fun3_t
{
  public:
explicit mem_fun3_t(_Ret (_Class::*_Pm)(_arg1,_arg2,_arg3))
    :m_Ptr(_Pm) //okay here we store the member function pointer for later use
    {}

//this operator call comes from the bind method
_Ret operator()(_Class *_P, _arg1 arg1, _arg2 arg2, _arg3 arg3) const
{
    return ((_P->*m_Ptr)(arg1,arg2,arg3));
}
private:
_Ret (_Class::*m_Ptr)(_arg1,_arg2,_arg3);// method pointer signature
};

aussi, nous avons besoin d'une méthode d'aide mem_fun3 pour la classe ci-dessus pour aider à appeler.

template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
mem_fun3_t<_Ret,_Class,_arg1,_arg2,_arg3> mem_fun3 ( _Ret (_Class::*_Pm)          (_arg1,_arg2,_arg3) )
{
  return (mem_fun3_t<_Ret,_Class,_arg1,_arg2,_arg3>(_Pm));

}

maintenant, pour lier les paramètres, nous devons écrire une fonction binder. Donc, ici, il va:

template<typename _Func,typename _Ptr,typename _arg1,typename _arg2,typename _arg3>
class binder3
{
public:
//This is the constructor that does the binding part
binder3(_Func fn,_Ptr ptr,_arg1 i,_arg2 j,_arg3 k)
    :m_ptr(ptr),m_fn(fn),m1(i),m2(j),m3(k){}

 //and this is the function object 
 void operator()() const
 {
        m_fn(m_ptr,m1,m2,m3);//that calls the operator
    }
private:
    _Ptr m_ptr;
    _Func m_fn;
    _arg1 m1; _arg2 m2; _arg3 m3;
};

et, un assistant fonction pour utiliser la classe binder3-bind3:

//a helper function to call binder3
template <typename _Func, typename _P1,typename _arg1,typename _arg2,typename _arg3>
binder3<_Func, _P1, _arg1, _arg2, _arg3> bind3(_Func func, _P1 p1,_arg1 i,_arg2 j,_arg3 k)
{
    return binder3<_Func, _P1, _arg1, _arg2, _arg3> (func, p1,i,j,k);
}

maintenant, nous devons utiliser ceci avec la classe de commande; utilisez le typedef suivant:

typedef binder3<mem_fun3_t<int,T,int,int,int> ,T* ,int,int,int> F3;
//and change the signature of the ctor
//just to illustrate the usage with a method signature taking more than one parameter
explicit Command(T* pObj,F3* p_method,long timeout,const char* key,
long priority = PRIO_NORMAL ):
m_objptr(pObj),m_timeout(timeout),m_key(key),m_value(priority),method1(0),method0(0),
method(0)
{
    method3 = p_method;
}

Voici comment vous l'appelez:

F3 f3 = PluginThreadPool::bind3( PluginThreadPool::mem_fun3( 
      &CTask::ThreeParameterTask), task1,2122,23 );

Note: f3 (); appellera la méthode task1->ThreeParameterTask (21,22,23);.

Le contexte de ce modèle, à la suite de lien

1
répondu Alex Punnen 2013-01-28 06:50:37

comme cela a été répété, les functors sont des classes qui peuvent être traitées comme des fonctions (overload operator ()).

ils sont les plus utiles pour les situations dans lesquelles vous devez associer certaines données avec des appels répétés ou retardés à une fonction.

par exemple, une liste de fonctions liées pourrait être utilisée pour mettre en œuvre un système de base de coroutine synchrone à basse fréquence, un répartiteur de tâches ou un parsing de fichiers interruptable. Exemples:

/* prints "this is a very simple and poorly used task queue" */
class Functor
{
public:
    std::string output;
    Functor(const std::string& out): output(out){}
    operator()() const
    {
        std::cout << output << " ";
    }
};

int main(int argc, char **argv)
{
    std::list<Functor> taskQueue;
    taskQueue.push_back(Functor("this"));
    taskQueue.push_back(Functor("is a"));
    taskQueue.push_back(Functor("very simple"));
    taskQueue.push_back(Functor("and poorly used"));
    taskQueue.push_back(Functor("task queue"));
    for(std::list<Functor>::iterator it = taskQueue.begin();
        it != taskQueue.end(); ++it)
    {
        *it();
    }
    return 0;
}

/* prints the value stored in "i", then asks you if you want to increment it */
int i;
bool should_increment;
int doSomeWork()
{
    std::cout << "i = " << i << std::endl;
    std::cout << "increment? (enter the number 1 to increment, 0 otherwise" << std::endl;
    std::cin >> should_increment;
    return 2;
}
void doSensitiveWork()
{
     ++i;
     should_increment = false;
}
class BaseCoroutine
{
public:
    BaseCoroutine(int stat): status(stat), waiting(false){}
    void operator()(){ status = perform(); }
    int getStatus() const { return status; }
protected:
    int status;
    bool waiting;
    virtual int perform() = 0;
    bool await_status(BaseCoroutine& other, int stat, int change)
    {
        if(!waiting)
        {
            waiting = true;
        }
        if(other.getStatus() == stat)
        {
            status = change;
            waiting = false;
        }
        return !waiting;
    }
}

class MyCoroutine1: public BaseCoroutine
{
public:
    MyCoroutine1(BaseCoroutine& other): BaseCoroutine(1), partner(other){}
protected:
    BaseCoroutine& partner;
    virtual int perform()
    {
        if(getStatus() == 1)
            return doSomeWork();
        if(getStatus() == 2)
        {
            if(await_status(partner, 1))
                return 1;
            else if(i == 100)
                return 0;
            else
                return 2;
        }
    }
};

class MyCoroutine2: public BaseCoroutine
{
public:
    MyCoroutine2(bool& work_signal): BaseCoroutine(1), ready(work_signal) {}
protected:
    bool& work_signal;
    virtual int perform()
    {
        if(i == 100)
            return 0;
        if(work_signal)
        {
            doSensitiveWork();
            return 2;
        }
        return 1;
    }
};

int main()
{
     std::list<BaseCoroutine* > coroutineList;
     MyCoroutine2 *incrementer = new MyCoroutine2(should_increment);
     MyCoroutine1 *printer = new MyCoroutine1(incrementer);

     while(coroutineList.size())
     {
         for(std::list<BaseCoroutine *>::iterator it = coroutineList.begin();
             it != coroutineList.end(); ++it)
         {
             *it();
             if(*it.getStatus() == 0)
             {
                 coroutineList.erase(it);
             }
         }
     }
     delete printer;
     delete incrementer;
     return 0;
}

bien sûr, ces exemples ne sont pas très utiles en eux-mêmes. Ils montrent seulement comment les foncteurs peuvent être utiles, les foncteurs eux-mêmes sont très basiques et inflexibles et cela les rend moins utiles que, par exemple, ce que boost fournit.

1
répondu nfries88 2014-02-08 05:34:56

un grand avantage de la mise en œuvre de fonctions en tant que functors est qu'ils peuvent maintenir et réutiliser l'état entre les appels. Par exemple, de nombreux algorithmes de programmation dynamique, comme le Wagner-Fischer algorithme pour le calcul de la distance Levenshtein entre les chaînes, travailler en remplissant un grand tableau des résultats. Il est très inefficace d'allouer cette table chaque fois que la fonction est appelée, donc mettre en œuvre la fonction en tant que functor et faire la variable table a member peut grandement améliorer la performance.

ci-dessous est un exemple de mise en œuvre de l'algorithme de Wagner-Fischer en tant que functor. Notez comment la table est affectée dans le constructeur, puis réutilisée dans operator() , avec redimensionnement si nécessaire.

#include <string>
#include <vector>
#include <algorithm>

template <typename T>
T min3(const T& a, const T& b, const T& c)
{
   return std::min(std::min(a, b), c);
}

class levenshtein_distance 
{
    mutable std::vector<std::vector<unsigned int> > matrix_;

public:
    explicit levenshtein_distance(size_t initial_size = 8)
        : matrix_(initial_size, std::vector<unsigned int>(initial_size))
    {
    }

    unsigned int operator()(const std::string& s, const std::string& t) const
    {
        const size_t m = s.size();
        const size_t n = t.size();
        // The distance between a string and the empty string is the string's length
        if (m == 0) {
            return n;
        }
        if (n == 0) {
            return m;
        }
        // Size the matrix as necessary
        if (matrix_.size() < m + 1) {
            matrix_.resize(m + 1, matrix_[0]);
        }
        if (matrix_[0].size() < n + 1) {
            for (auto& mat : matrix_) {
                mat.resize(n + 1);
            }
        }
        // The top row and left column are prefixes that can be reached by
        // insertions and deletions alone
        unsigned int i, j;
        for (i = 1;  i <= m; ++i) {
            matrix_[i][0] = i;
        }
        for (j = 1; j <= n; ++j) {
            matrix_[0][j] = j;
        }
        // Fill in the rest of the matrix
        for (j = 1; j <= n; ++j) {
            for (i = 1; i <= m; ++i) {
                unsigned int substitution_cost = s[i - 1] == t[j - 1] ? 0 : 1;
                matrix_[i][j] =
                    min3(matrix_[i - 1][j] + 1,                 // Deletion
                    matrix_[i][j - 1] + 1,                      // Insertion
                    matrix_[i - 1][j - 1] + substitution_cost); // Substitution
            }
        }
        return matrix_[m][n];
    }
};
1
répondu Martin Broadhurst 2017-01-16 09:56:58

Functor peut également être utilisé pour simuler la définition d'une fonction locale au sein d'une fonction. Se référer à la question et un autre .

mais un foncteur local ne peut pas accéder à l'extérieur des variables automatiques. La fonction lambda (C++11) est une meilleure solution.

0
répondu Shawn Xie 2017-05-23 12:34:53

j'ai "découvert" une utilisation très intéressante de foncteurs: je les utilise quand je n'ai pas un bon nom pour une méthode, comme un foncteur est une méthode sans nom ;-)

-9
répondu JChMathae 2011-02-16 13:29:25