Question

Dans la bibliothèque Boost Signals , ils surchargez l'opérateur ().

Est-ce une convention en C ++? Pour les rappels, etc.?

J'ai vu cela dans le code d'un collègue (qui se trouve être un grand fan de Boost). Parmi tous les bienfaits de Boost, cela n'a pour moi que semé la confusion.

Avez-vous un aperçu de la raison de cette surcharge?

Était-ce utile?

La solution

L’un des principaux objectifs de la surcharge de operator () est de créer un foncteur. Un foncteur agit comme une fonction, mais il présente les avantages qu’il possède comme état, ce qui signifie qu’il peut conserver des données reflétant son état entre les appels.

Voici un exemple de foncteur simple:

struct Accumulator
{
    int counter = 0;
    int operator()(int i) { return counter += i; }
}
...
Accumulator acc;
cout << acc(10) << endl; //prints "10"
cout << acc(20) << endl; //prints "30"

Les foncteurs sont fortement utilisés avec la programmation générique. De nombreux algorithmes STL sont écrits de manière très générale, de sorte que vous pouvez brancher votre propre fonction / foncteur dans l’algorithme. Par exemple, l'algorithme std :: for_each vous permet d'appliquer une opération sur chaque élément d'une plage. Il pourrait être mis en œuvre quelque chose comme ça:

template <typename InputIterator, typename Functor>
void for_each(InputIterator first, InputIterator last, Functor f)
{
    while (first != last) f(*first++);
}

Vous voyez que cet algorithme est très générique puisqu'il est paramétré par une fonction. En utilisant l’opérateur (), cette fonction vous permet d’utiliser un foncteur ou un pointeur de fonction. Voici un exemple montrant les deux possibilités:

void print(int i) { std::cout << i << std::endl; }
...    
std::vector<int> vec;
// Fill vec

// Using a functor
Accumulator acc;
std::for_each(vec.begin(), vec.end(), acc);
// acc.counter contains the sum of all elements of the vector

// Using a function pointer
std::for_each(vec.begin(), vec.end(), print); // prints all elements

Concernant votre question sur la surcharge de operator (), eh bien oui, c’est possible. Vous pouvez parfaitement écrire un foncteur avec plusieurs opérateurs de parenthèses, à condition de respecter les règles de base de la surcharge de méthodes (par exemple, la surcharge uniquement sur le type de résultat est impossible).

Autres conseils

Cela permet à une classe d'agir comme une fonction. Je l’ai utilisé dans une classe de journalisation où l’appel devrait être une fonction, mais je voulais profiter des avantages supplémentaires de la classe.

donc quelque chose comme ça:

logger.log("Log this message");

se transforme en ceci:

logger("Log this message");

Beaucoup ont répondu que c’était un foncteur, sans expliquer en grande partie pourquoi un foncteur était meilleur qu’une simple fonction ancienne.

La réponse est qu’un foncteur peut avoir un état. Envisagez une fonction de sommation - elle doit conserver un total cumulé.

class Sum
{
public:
    Sum() : m_total(0)
    {
    }
    void operator()(int value)
    {
        m_total += value;
    }
    int m_total;
};

Un foncteur n’est pas une fonction, vous ne pouvez donc pas le surcharger.
Votre collègue a cependant raison de dire que la surcharge de operator () est utilisée pour créer des "foncteurs". - objets qui peuvent être appelés comme des fonctions. En combinaison avec des modèles dans l’attente de " fonction-like " arguments cela peut être assez puissant car la distinction entre un objet et une fonction devient floue.

Comme d'autres affiches l'ont dit: les foncteurs ont un avantage sur les fonctions simples en ce sens qu'ils peuvent avoir un état. Cet état peut être utilisé sur une seule itération (par exemple, pour calculer la somme de tous les éléments d'un conteneur) ou sur plusieurs itérations (par exemple, pour rechercher tous les éléments de plusieurs conteneurs répondant à des critères particuliers).

Commencez à utiliser std :: for_each , std :: find_if , etc. plus souvent dans votre code et vous verrez pourquoi il est pratique de pouvoir surcharger l'opérateur. Il permet également aux foncteurs et aux tâches d’avoir une méthode d’appel claire qui n’entrera pas en conflit avec les noms des autres méthodes des classes dérivées.

Vous pouvez également consulter la Matrice C ++ faq exemple . Cela peut être utile, mais cela dépend bien sûr de ce que vous essayez d'accomplir.

Les foncteurs sont fondamentalement des indicateurs de fonction. Ils sont généralement conçus pour être copiés (comme les pointeurs de fonction) et appelés de la même manière que les pointeurs de fonction. Le principal avantage est que lorsque vous utilisez un algorithme fonctionnant avec un foncteur basé sur un modèle, l'appel de fonction à operator () peut être inséré. Cependant, les pointeurs de fonction sont toujours des foncteurs valides.

L'utilisation de operator () pour former les foncteurs en C ++ est liée à paradigmes de programmation fonctionnelle qui utilisent généralement un concept similaire: fermetures .

Une force que je peux voir, cependant, cela peut être discuté, est que la signature de operator () ressemble et se comporte de la même manière dans différents types. Si nous avions une classe Reporter qui avait un rapport de méthode membre (..), puis une autre classe Writer, qui avait une méthode membre write (..), nous devrions écrire des adaptateurs si nous souhaitons utiliser les deux classes comme suit: un composant de modèle d'un autre système. Tout ce qui compte, c’est de passer des cordes ou ce que vous avez. Sans l'utilisation de l'opérateur () surcharger ou écrire des adaptateurs de type spécial, vous ne pourriez pas faire des choses comme

T t;
t.write("Hello world");

parce que T exige qu’il existe une fonction membre appelée write qui accepte tout ce qui peut être implicitement converti en const char * (ou plutôt const char []). La classe Reporter de cet exemple n’a pas cela, alors la compilation de T (un paramètre de modèle) étant impossible, elle ne serait pas compilée.

Cependant, autant que je sache, cela fonctionnerait avec différents types

T t;
t("Hello world");

Cependant, cela exige toujours explicitement que le type T ait un tel opérateur défini. Nous avons donc toujours une exigence sur T. Personnellement, je ne pense pas que ce soit trop bizarre avec les foncteurs tels qu'ils sont couramment utilisés, mais je préférerais voir d'autres mécanismes pour ce comportement. Dans des langages comme C #, vous pouvez simplement faire passer un délégué. Je ne connais pas très bien les pointeurs de fonction de membre en C ++, mais j’imagine que vous pouvez également obtenir le même comportement.

À part le comportement des sucres syntatiques, je ne vois pas vraiment les avantages de la surcharge par l'opérateur pour effectuer de telles tâches.

Je suis sûr qu'il y a plus de personnes sciemment qui ont de meilleures raisons que moi, mais je pensais partager mon opinion pour que le reste d'entre vous puisse la partager.

Un autre collègue a souligné que cela pourrait être un moyen de déguiser des objets de foncteur en fonctions. Par exemple, ceci:

my_functor();

est vraiment:

my_functor.operator()();

Cela signifie-t-il ceci:

my_functor(int n, float f){ ... };

Peut-on aussi surcharger ce produit?

my_functor.operator()(int n, float f){ ... };

D'autres publications ont bien décrit le fonctionnement de operator () et son utilité.

J'ai récemment utilisé un code qui fait très largement appel à operator (). L’inconvénient de surcharger cet opérateur est que certains IDE deviennent des outils moins efficaces. Dans Visual Studio, vous pouvez généralement cliquer avec le bouton droit de la souris sur un appel de méthode pour accéder à la définition et / ou à la déclaration de la méthode. Malheureusement, VS n’est pas assez intelligent pour indexer les appels de l’opérateur (). Surtout dans le code complexe avec des définitions d'opérateur () écrasées partout, il peut être très difficile de déterminer quel morceau de code est exécuté où. Dans plusieurs cas, j'ai découvert que je devais exécuter le code et le parcourir pour rechercher ce qui fonctionnait réellement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top