c++ - une - tableau de pointeur sur fonction exemple




Fonctions de rappel en c++ (6)

Il n'y a pas de concept explicite d'une fonction de rappel en C ++. Les mécanismes de rappel sont souvent implémentés via des pointeurs de fonction, des objets foncteurs ou des objets de rappel. Les programmeurs doivent explicitement concevoir et implémenter la fonctionnalité de rappel.

Modifier en fonction des commentaires:

Malgré les commentaires négatifs que cette réponse a reçus, ce n'est pas faux. Je vais essayer de mieux expliquer d'où je viens.

C et C ++ ont tout ce dont vous avez besoin pour implémenter des fonctions de rappel. La manière la plus courante et la plus triviale d'implémenter une fonction de rappel est de passer un pointeur de fonction en tant qu'argument de fonction.

Cependant, les fonctions de rappel et les pointeurs de fonction ne sont pas synonymes. Un pointeur de fonction est un mécanisme de langage, tandis qu'une fonction de rappel est un concept sémantique. Les pointeurs de fonction ne sont pas la seule façon d'implémenter une fonction de rappel - vous pouvez également utiliser des foncteurs et même des fonctions virtuelles de type jardin. Ce qui fait qu'une fonction appelle un rappel n'est pas le mécanisme utilisé pour identifier et appeler la fonction, mais le contexte et la sémantique de l'appel. Dire que quelque chose est une fonction de rappel implique une séparation supérieure à la normale entre la fonction appelante et la fonction spécifique appelée, un couplage conceptuel plus lâche entre l'appelant et l'appelé, l'appelant ayant un contrôle explicite sur ce qui est appelé. C'est cette notion floue de couplage conceptuel plus lâche et de sélection de fonction pilotée par l'appelant qui fait de quelque chose une fonction de rappel, et non l'utilisation d'un pointeur de fonction.

Par exemple, la documentation .NET pour IFormatProvider indique que "GetFormat est une méthode de rappel" , même s'il ne s'agit que d'une méthode d'interface "run-of-the-mill". Je ne pense pas que quiconque puisse prétendre que tous les appels de méthode virtuelle sont des fonctions de rappel. Ce qui fait de GetFormat une méthode de rappel n'est pas la mécanique de la façon dont il est transmis ou invoqué, mais la sémantique de l'appelant qui choisit la méthode GetFormat de l'objet sera appelée.

Certaines langues incluent des fonctionnalités avec une sémantique de rappel explicite, généralement liée aux événements et à la gestion des événements. Par exemple, C # a le type d' événement avec syntaxe et sémantique explicitement conçu autour du concept de callbacks. Visual Basic a sa clause Handles , qui déclare explicitement une méthode pour être une fonction de rappel tout en abstrayant le concept de délégués ou de pointeurs de fonction. Dans ces cas, le concept sémantique d'un rappel est intégré dans le langage lui-même.

C et C ++, d'autre part, n'incorpore pas le concept sémantique des fonctions de rappel presque aussi explicitement. Les mécanismes sont là, la sémantique intégrée ne l'est pas. Vous pouvez très bien implémenter des fonctions de rappel, mais pour obtenir quelque chose de plus sophistiqué qui inclut une sémantique de rappel explicite, vous devez le construire en plus de ce que C ++ fournit, comme ce que Qt a fait avec ses Signals and Slots .

En un mot, C ++ a ce dont vous avez besoin pour implémenter des rappels, souvent assez facilement et trivialement en utilisant des pointeurs de fonction. Ce qu'il n'a pas, ce sont les mots-clés et les fonctionnalités dont la sémantique est spécifique aux callbacks, comme raise , emit , Handles , event + = , etc. Si vous venez d'un langage avec ces types d'éléments, le callback natif supporte le C ++ se sentira stérilisé.

En c ++, quand et comment utilisez-vous une fonction de rappel?

MODIFIER:
Je voudrais voir un exemple simple pour écrire une fonction de rappel.


Il y a aussi la façon de faire les rappels en C: les pointeurs de fonction

//Define a type for the callback signature,
//it is not necessary, but makes life easier

//Function pointer called CallbackType that takes a float
//and returns an int
typedef int (*CallbackType)(float);  


void DoWork(CallbackType callback)
{
  float variable = 0.0f;

  //Do calculations

  //Call the callback with the variable, and retrieve the
  //result
  int result = callback(variable);

  //Do something with the result
}

int SomeCallback(float variable)
{
  int result;

  //Interpret variable

  return result;
}

int main(int argc, char ** argv)
{
  //Pass in SomeCallback to the DoWork
  DoWork(&SomeCallback);
}

Maintenant, si vous voulez passer des méthodes de classe en tant que callbacks, les déclarations à ces pointeurs de fonction ont des déclarations plus complexes, par exemple:

//Declaration:
typedef int (ClassName::*CallbackType)(float);

//This method performs work using an object instance
void DoWorkObject(CallbackType callback)
{
  //Class instance to invoke it through
  ClassName objectInstance;

  //Invocation
  int result = (objectInstance.*callback)(1.0f);
}

//This method performs work using an object pointer
void DoWorkPointer(CallbackType callback)
{
  //Class pointer to invoke it through
  ClassName * pointerInstance;

  //Invocation
  int result = (pointerInstance->*callback)(1.0f);
}

int main(int argc, char ** argv)
{
  //Pass in SomeCallback to the DoWork
  DoWorkObject(&ClassName::Method);
  DoWorkPointer(&ClassName::Method);
}

Les fonctions de rappel font partie de la norme C, donc aussi une partie de C ++. Mais si vous travaillez avec C ++, je vous suggère d'utiliser le modèle d'observateur à la place: http://en.wikipedia.org/wiki/Observer_pattern


Scott Meyers donne un bel exemple:

class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);

class GameCharacter
{
public:
  typedef std::function<int (const GameCharacter&)> HealthCalcFunc;

  explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
  : healthFunc(hcf)
  { }

  int healthValue() const { return healthFunc(*this); }

private:
  HealthCalcFunc healthFunc;
};

Je pense que l'exemple dit tout.

std::function<> est la manière "moderne" d'écrire des callbacks C ++.


Voir la définition ci-dessus où il est indiqué qu'une fonction de rappel est transmise à une autre fonction et qu'elle est appelée à un moment donné.

En C ++, il est souhaitable que les fonctions de callback appellent une méthode classes. Lorsque vous faites cela, vous avez accès aux données des membres. Si vous utilisez la méthode C pour définir un rappel, vous devrez le pointer vers une fonction membre statique. Ce n'est pas très souhaitable.

Voici comment vous pouvez utiliser les callbacks en C ++. Supposons 4 fichiers. Une paire de fichiers .CPP / .H pour chaque classe. La classe C1 est la classe avec une méthode que nous voulons rappeler. C2 rappelle la méthode de C1. Dans cet exemple, la fonction de rappel prend 1 paramètre que j'ai ajouté dans l'intérêt du lecteur. L'exemple ne montre aucun objet instancié et utilisé. Un cas d'utilisation de cette implémentation est lorsque vous avez une classe qui lit et stocke des données dans l'espace temporaire et une autre qui traite les données. Avec une fonction de rappel, pour chaque ligne de données lue, le callback peut alors le traiter. Cette technique coupe les frais généraux de l'espace temporaire requis. Il est particulièrement utile pour les requêtes SQL qui renvoient une grande quantité de données qui doivent ensuite être post-traitées.

/////////////////////////////////////////////////////////////////////
// C1 H file

class C1
{
    public:
    C1() {};
    ~C1() {};
    void CALLBACK F1(int i);
};

/////////////////////////////////////////////////////////////////////
// C1 CPP file

void CALLBACK C1::F1(int i)
{
// Do stuff with C1, its methods and data, and even do stuff with the passed in parameter
}

/////////////////////////////////////////////////////////////////////
// C2 H File

class C1; // Forward declaration

class C2
{
    typedef void (CALLBACK C1::* pfnCallBack)(int i);
public:
    C2() {};
    ~C2() {};

    void Fn(C1 * pThat,pfnCallBack pFn);
};

/////////////////////////////////////////////////////////////////////
// C2 CPP File

void C2::Fn(C1 * pThat,pfnCallBack pFn)
{
    // Call a non-static method in C1
    int i = 1;
    (pThat->*pFn)(i);
}

Note: La plupart des réponses couvrent les pointeurs de fonction, ce qui est une possibilité d'obtenir une logique de "callback" en C ++, mais à ce jour, ce n'est pas la plus favorable que je pense.

Quels sont les rappels (?) Et pourquoi les utiliser (!)

Un rappel est un callable (voir plus bas) accepté par une classe ou une fonction, utilisé pour personnaliser la logique actuelle en fonction de ce rappel.

Une des raisons d'utiliser les callbacks est d'écrire du code générique qui est indépendant de la logique dans la fonction appelée et qui peut être réutilisé avec différents callbacks.

De nombreuses fonctions de la bibliothèque d'algorithmes standard <algorithm> utilisent des rappels. Par exemple, l'algorithme for_each applique un rappel unaire à chaque élément d'une plage d'itérateurs:

template<class InputIt, class UnaryFunction>
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f)
{
  for (; first != last; ++first) {
    f(*first);
  }
  return f;
}

qui peut être utilisé pour d'abord incrémenter et ensuite imprimer un vecteur en passant les callables appropriés par exemple:

std::vector<double> v{ 1.0, 2.2, 4.0, 5.5, 7.2 };
double r = 4.0;
std::for_each(v.begin(), v.end(), [&](double & v) { v += r; });
std::for_each(v.begin(), v.end(), [](double v) { std::cout << v << " "; });

qui imprime

5 6.2 8 9.5 11.2

Une autre application des callbacks est la notification des appelants de certains événements qui permet une certaine flexibilité statique / temps de compilation.

Personnellement, j'utilise une bibliothèque d'optimisation locale qui utilise deux callbacks différents:

  • Le premier rappel est appelé si une valeur de fonction et le gradient basé sur un vecteur de valeurs d'entrée est requis (rappel logique: détermination de la valeur de la fonction / dérivation du gradient).
  • Le second rappel est appelé une fois pour chaque étape de l'algorithme et reçoit certaines informations sur la convergence de l'algorithme (rappel de notification).

Ainsi, le concepteur de bibliothèque n'est pas chargé de décider de ce qui se passe avec les informations fournies au programmeur via le rappel de notification et il n'a pas besoin de s'inquiéter de la détermination des valeurs de fonction car elles sont fournies par le rappel logique. Obtenir ces choses correctement est une tâche due à l'utilisateur de la bibliothèque et maintient la bibliothèque mince et plus générique.

De plus, les rappels peuvent activer le comportement d'exécution dynamique.

Imaginez une sorte de classe de moteur de jeu qui a une fonction qui est déclenchée, chaque fois que les utilisateurs pressent un bouton sur son clavier et un ensemble de fonctions qui contrôlent votre comportement de jeu. Avec les rappels, vous pouvez (re) décider à l'exécution quelle action sera entreprise.

void player_jump();
void player_crouch();

class game_core
{
    std::array<void(*)(), total_num_keys> actions;
    // 
    void key_pressed(unsigned key_id)
    {
        if(actions[key_id]) actions[key_id]();
    }
    // update keybind from menu
    void update_keybind(unsigned key_id, void(*new_action)())
    {
        actions[key_id] = new_action;
    }
};

Ici, la fonction key_pressed utilise les callbacks stockés dans les actions pour obtenir le comportement désiré lorsqu'une certaine touche est enfoncée. Si le joueur choisit de changer le bouton pour sauter, le moteur peut appeler

game_core_instance.update_keybind(newly_selected_key, &player_jump);

et ainsi changer le comportement d'un appel à key_pressed (que les appels player_jump ) une fois ce bouton est pressé la prochaine fois dans le jeu.

Quels sont les callables en C ++ (11)?

Voir Concepts C ++: Callable sur cppreference pour une description plus formelle.

La fonctionnalité de rappel peut être réalisée de plusieurs façons en C ++ (11) car plusieurs choses différentes peuvent être appelées * :

  • Pointeurs de fonction (y compris les pointeurs vers les fonctions membres)
  • std::function objets de std::function
  • Expressions lambda
  • Les expressions de liaison
  • Objets fonction (classes avec opérateur d'appel de fonction surchargé operator() )

* Remarque: Le pointeur vers les membres de données est également appelable mais aucune fonction n'est appelée du tout.

Plusieurs façons importantes d'écrire des rappels en détail

  • X.1 "Ecrire" un rappel dans ce message signifie la syntaxe pour déclarer et nommer le type de rappel.
  • X.2 "Appeler" un rappel fait référence à la syntaxe pour appeler ces objets.
  • X.3 «Utiliser» un rappel signifie la syntaxe lors de la transmission d'arguments à une fonction à l'aide d'un rappel.

Note: A partir de C ++ 17, un appel comme f(...) peut être écrit comme std::invoke(f, ...) qui gère aussi le pointeur sur case member.

1. Pointeurs de fonction

Un pointeur de fonction est le type le plus simple (en termes de généralité, en termes de lisibilité, c'est le pire) qu'un callback peut avoir.

Ayons une fonction simple foo :

int foo (int x) { return 2+x; }

1.1 Ecriture d'un pointeur de fonction / notation de type

Un type de pointeur de fonction a la notation

return_type (*)(paramter_type_1, paramter_type_2, paramter_type_3)
// i.e. a pointer to foo has the type:
int (*)(int)

où un type de pointeur de fonction nommé ressemblera

return_type (* name) (paramter_type_1, paramter_type_2, paramter_type_3)

// i.e. f_int_t is a type: function pointer taking one int argument, returning int
typedef int (*f_int_t) (int); 

// foo_p is a pointer to function taking int returning int
// initialized by pointer to function foo taking int returning int
int (* foo_p)(int) = &foo; 
// can alternatively be written as 
f_int_t foo_p = &foo;

La déclaration using nous donne l'option de rendre les choses un peu plus lisibles, puisque le typedef pour f_int_t peut aussi être écrit comme:

using f_int_t = int(*)(int);

Où (au moins pour moi) il est plus clair que f_int_t est le nouveau type alias et la reconnaissance du type de pointeur de fonction est aussi plus facile

Et une déclaration d'une fonction utilisant un callback de type pointeur de fonction sera:

// foobar having a callback argument named moo of type 
// pointer to function returning int taking int as its argument
int foobar (int x, int (*moo)(int));
// if f_int is the function pointer typedef from above we can also write foobar as:
int foobar (int x, f_int_t moo);

1.2 Notation d'appel de rappel

La notation d'appel suit la syntaxe d'appel de fonction simple:

int foobar (int x, int (*moo)(int))
{
    return x + moo(x); // function pointer moo called using argument x
}
// analog
int foobar (int x, f_int_t moo)
{
    return x + moo(x); // function pointer moo called using argument x
}

1.3 Notation d'utilisation du rappel et types compatibles

Une fonction de rappel prenant un pointeur de fonction peut être appelée à l'aide de pointeurs de fonction.

Utiliser une fonction qui prend un rappel de pointeur de fonction est plutôt simple:

 int a = 5;
 int b = foobar(a, foo); // call foobar with pointer to foo as callback
 // can also be
 int b = foobar(a, &foo); // call foobar with pointer to foo as callback

1.4 Exemple

Une fonction ca peut être écrite qui ne dépend pas du fonctionnement du callback:

void tranform_every_int(int * v, unsigned n, int (*fp)(int))
{
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = fp(v[i]);
  }
}

si possible, les rappels peuvent être

int double_int(int x) { return 2*x; }
int square_int(int x) { return x*x; }

utilisé comme

int a[5] = {1, 2, 3, 4, 5};
tranform_every_int(&a[0], 5, double_int);
// now a == {2, 4, 6, 8, 10};
tranform_every_int(&a[0], 5, square_int);
// now a == {4, 16, 36, 64, 100};

2. Pointeur à la fonction de membre

Un pointeur sur la fonction membre (d'une certaine classe C ) est un type spécial de pointeur de fonction (et encore plus complexe) qui nécessite un objet de type C pour fonctionner.

struct C
{
    int y;
    int foo(int x) const { return x+y; }
};

2.1 Pointeur d'écriture sur la notation de fonction / type de membre

Un pointeur sur le type de fonction membre pour une classe T a la notation

// can have more or less parameters
return_type (T::*)(paramter_type_1, paramter_type_2, paramter_type_3)
// i.e. a pointer to C::foo has the type
int (C::*) (int)

où un pointeur nommé vers la fonction membre ressemblera à ceci:

return_type (T::* name) (paramter_type_1, paramter_type_2, paramter_type_3)

// i.e. a type `f_C_int` representing a pointer to member function of `C`
// taking int returning int is:
typedef int (C::* f_C_int_t) (int x); 

// The type of C_foo_p is a pointer to member function of C taking int returning int
// Its value is initialized by a pointer to foo of C
int (C::* C_foo_p)(int) = &C::foo;
// which can also be written using the typedef:
f_C_int_t C_foo_p = &C::foo;

Exemple: déclarer une fonction en prenant un pointeur sur le callback de la fonction membre comme l'un de ses arguments:

// C_foobar having an argument named moo of type pointer to member function of C
// where the callback returns int taking int as its argument
// also needs an object of type c
int C_foobar (int x, C const &c, int (C::*moo)(int));
// can equivalently declared using the typedef above:
int C_foobar (int x, C const &c, f_C_int_t moo);

2.2 Notation d'appel de rappel

Le pointeur vers la fonction membre de C peut être invoqué, par rapport à un objet de type C en utilisant des opérations d'accès de membre sur le pointeur déréférencé. Note: Parenthèses requises!

int C_foobar (int x, C const &c, int (C::*moo)(int))
{
    return x + (c.*moo)(x); // function pointer moo called for object c using argument x
}
// analog
int C_foobar (int x, C const &c, f_C_int_t moo)
{
    return x + (c.*moo)(x); // function pointer moo called for object c using argument x
}

Note: Si un pointeur vers C est disponible, la syntaxe est équivalente (où le pointeur sur C doit être déréférencé aussi):

int C_foobar_2 (int x, C const * c, int (C::*meow)(int))
{
    if (!c) return x;
    // function pointer meow called for object *c using argument x
    return x + ((*c).*meow)(x); 
}
// or equivalent:
int C_foobar_2 (int x, C const * c, int (C::*meow)(int))
{
    if (!c) return x;
    // function pointer meow called for object *c using argument x
    return x + (c->*meow)(x); 
}

2.3 Notation d'utilisation du rappel et types compatibles

Une fonction de rappel prenant un pointeur de fonction membre de classe T peut être appelée en utilisant un pointeur de fonction membre de classe T

L'utilisation d'une fonction qui prend un pointeur sur le rappel de la fonction membre est également très simple, par analogie avec les pointeurs de fonction.

 C my_c{2}; // aggregate initialization
 int a = 5;
 int b = C_foobar(a, my_c, &C::foo); // call C_foobar with pointer to foo as its callback

3. objets std::function (en-tête <functional> )

La classe std::function est un wrapper de fonction polymorphe pour stocker, copier ou appeler des callables.

3.1 Ecrire une notation std::function object / type

Le type d'un objet std::function stockant un callable ressemble à:

std::function<return_type(paramter_type_1, paramter_type_2, paramter_type_3)>

// i.e. using the above function declaration of foo:
std::function<int(int)> stdf_foo = &foo;
// or C::foo:
std::function<int(const C&, int)> stdf_C_foo = &C::foo;

3.2 Notation d'appel de rappel

La classe std::function a operator() defined qui peut être utilisé pour invoquer sa cible.

int stdf_foobar (int x, std::function<int(int)> moo)
{
    return x + moo(x); // std::function moo called
}
// or 
int stdf_C_foobar (int x, C const &c, std::function<int(C const &, int)> moo)
{
    return x + moo(c, x); // std::function moo called using c and x
}

3.3 Notation d'utilisation du rappel et types compatibles

Le rappel std::function est plus générique que les pointeurs de fonction ou le pointeur vers la fonction de membre puisque différents types peuvent être passés et convertis implicitement en un objet std::function .

3.3.1 Pointeurs de fonction et pointeurs vers les fonctions membres

Un pointeur de fonction

int a = 2;
int b = stdf_foobar(a, &foo);
// b == 6 ( 2 + (2+2) )

ou un pointeur vers la fonction membre

int a = 2;
C my_c{7}; // aggregate initialization
int b = stdf_C_foobar(a, c, &C::foo);
// b == 11 == ( 2 + (7+2) )

peut être utilisé.

3.3.2 Expressions lambda

Une fermeture sans nom d'une expression lambda peut être stockée dans un objet std::function :

int a = 2;
int c = 3;
int b = stdf_foobar(a, [c](int x) -> int { return 7+c*x; });
// b == 15 ==  a + (7*c*a) == 2 + (7+3*2)

3.3.3 expressions std::bind

Le résultat d'une expression std::bind peut être passé. Par exemple en liant des paramètres à un appel de pointeur de fonction:

int foo_2 (int x, int y) { return 9*x + y; }
using std::placeholders::_1;

int a = 2;
int b = stdf_foobar(a, std::bind(foo_2, _1, 3));
// b == 23 == 2 + ( 9*2 + 3 )
int c = stdf_foobar(a, std::bind(foo_2, 5, _1));
// c == 49 == 2 + ( 9*5 + 2 )

Où aussi les objets peuvent être liés en tant qu'objet pour l'invocation de pointeur sur les fonctions membres:

int a = 2;
C const my_c{7}; // aggregate initialization
int b = stdf_foobar(a, std::bind(&C::foo, my_c, _1));
// b == 1 == 2 + ( 2 + 7 )

3.3.4 Objets fonctionnels

Les objets de classes ayant une surcharge d' operator() peuvent également être stockés dans un objet std::function .

struct Meow
{
  int y = 0;
  Meow(int y_) : y(y_) {}
  int operator()(int x) { return y * x; }
};
int a = 11;
int b = stdf_foobar(a, Meow{8});
// b == 99 == 11 + ( 8 * 11 )

3.4 Exemple

Changer l'exemple du pointeur de fonction pour utiliser std::function

void stdf_tranform_every_int(int * v, unsigned n, std::function<int(int)> fp)
{
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = fp(v[i]);
  }
}

donne beaucoup plus d'utilité à cette fonction parce que (voir 3.3) nous avons plus de possibilités pour l'utiliser:

// using function pointer still possible
int a[5] = {1, 2, 3, 4, 5};
stdf_tranform_every_int(&a[0], 5, double_int);
// now a == {2, 4, 6, 8, 10};

// use it without having to write another function by using a lambda
stdf_tranform_every_int(&a[0], 5, [](int x) -> int { return x/2; });
// now a == {1, 2, 3, 4, 5}; again

// use std::bind :
int nine_x_and_y (int x, int y) { return 9*x + y; }
using std::placeholders::_1;
// calls nine_x_and_y for every int in a with y being 4 every time
stdf_tranform_every_int(&a[0], 5, std::bind(nine_x_and_y, _1, 4));
// now a == {13, 22, 31, 40, 49};

4. Type de rappel basé sur un modèle

À l'aide de modèles, le code appelant le rappel peut être encore plus général que l'utilisation std::function objets std::function .

Notez que les modèles sont une fonctionnalité de compilation et sont un outil de conception pour le polymorphisme de compilation. Si le comportement dynamique d'exécution doit être obtenu par le biais de rappels, les modèles seront utiles, mais ils n'induiront pas de dynamique d'exécution.

4.1 Écrire (notations de type) et appeler des rappels modélisés

Généraliser ie encore le code std_ftransform_every_int d'en haut peut être réalisé en utilisant des modèles:

template<class R, class T>
void stdf_transform_every_int_templ(int * v,
  unsigned const n, std::function<R(T)> fp)
{
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = fp(v[i]);
  }
}

avec une syntaxe encore plus générale (ainsi que la plus simple) pour un type de callback étant un argument basé sur un template, à déduire:

template<class F>
void transform_every_int_templ(int * v, 
  unsigned const n, F f)
{
  std::cout << "transform_every_int_templ<" 
    << type_name<F>() << ">\n";
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = f(v[i]);
  }
}

Remarque: La sortie incluse imprime le nom de type déduit pour le type de modèle F L'implémentation de type_name est donnée à la fin de ce post.

L'implémentation la plus générale pour la transformation unaire d'une gamme fait partie de la bibliothèque standard, à savoir std::transform , qui est également modélisée par rapport aux types itérés.

template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first,
  UnaryOperation unary_op)
{
  while (first1 != last1) {
    *d_first++ = unary_op(*first1++);
  }
  return d_first;
}

4.2 Exemples utilisant des rappels modélisés et des types compatibles

Les types compatibles pour la méthode de rappel std::function stdf_transform_every_int_templ sont identiques aux types mentionnés ci-dessus (voir 3.4).

En utilisant la version modèle, la signature du rappel utilisé peut changer un peu:

// Let
int foo (int x) { return 2+x; }
int muh (int const &x) { return 3+x; }
int & woof (int &x) { x *= 4; return x; }

int a[5] = {1, 2, 3, 4, 5};
stdf_transform_every_int_templ<int,int>(&a[0], 5, &foo);
// a == {3, 4, 5, 6, 7}
stdf_transform_every_int_templ<int, int const &>(&a[0], 5, &muh);
// a == {6, 7, 8, 9, 10}
stdf_transform_every_int_templ<int, int &>(&a[0], 5, &woof);

Note: std_ftransform_every_int (version non std_ftransform_every_int , voir ci-dessus) fonctionne avec foo mais n'utilise pas muh .

// Let
void print_int(int * p, unsigned const n)
{
  bool f{ true };
  for (unsigned i = 0; i < n; ++i)
  {
    std::cout << (f ? "" : " ") << p[i]; 
    f = false;
  }
  std::cout << "\n";
}

Le paramètre de template simple de transform_every_int_templ peut être tous les types appelables possibles.

int a[5] = { 1, 2, 3, 4, 5 };
print_int(a, 5);
transform_every_int_templ(&a[0], 5, foo);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, muh);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, woof);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, [](int x) -> int { return x + x + x; });
print_int(a, 5);
transform_every_int_templ(&a[0], 5, Meow{ 4 });
print_int(a, 5);
using std::placeholders::_1;
transform_every_int_templ(&a[0], 5, std::bind(foo_2, _1, 3));
print_int(a, 5);
transform_every_int_templ(&a[0], 5, std::function<int(int)>{&foo});
print_int(a, 5);

Le code ci-dessus imprime:

1 2 3 4 5
transform_every_int_templ <int(*)(int)>
3 4 5 6 7
transform_every_int_templ <int(*)(int&)>
6 8 10 12 14
transform_every_int_templ <int& (*)(int&)>
9 11 13 15 17
transform_every_int_templ <main::{lambda(int)#1} >
27 33 39 45 51
transform_every_int_templ <Meow>
108 132 156 180 204
transform_every_int_templ <std::_Bind<int(*(std::_Placeholder<1>, int))(int, int)>>
975 1191 1407 1623 1839
transform_every_int_templ <std::function<int(int)>>
977 1193 1409 1625 1841

implémentation type_name utilisée ci-dessus

#include <type_traits>
#include <typeinfo>
#include <string>
#include <memory>
#include <cxxabi.h>

template <class T>
std::string type_name()
{
  typedef typename std::remove_reference<T>::type TR;
  std::unique_ptr<char, void(*)(void*)> own
    (abi::__cxa_demangle(typeid(TR).name(), nullptr,
    nullptr, nullptr), std::free);
  std::string r = own != nullptr?own.get():typeid(TR).name();
  if (std::is_const<TR>::value)
    r += " const";
  if (std::is_volatile<TR>::value)
    r += " volatile";
  if (std::is_lvalue_reference<T>::value)
    r += " &";
  else if (std::is_rvalue_reference<T>::value)
    r += " &&";
  return r;
}






function-pointers