Quel est le type d’argument correct pour un object fonction?

J’ai une fonction basée sur un modèle qui reçoit des objects fonction. Parfois, les objects fonction sont des structures sans état, mais parfois ils sont de grands objects statefull. L’état de la fonction-object n’est pas modifié dans cette fonction, seulement examiné. Je suis également très intéressé par l’écriture de code que le compilateur peut optimiser autant que possible. Que dois-je considérer lors du choix du type d’argument?

La fonction est de ce type:

template auto make_particle(funcT fun) { Particle particle; particle = fun(); return particle; } 

Le type d’argument devrait probablement être funcT const & fun afin que les gros objects ne soient pas copiés, mais pourquoi la plupart des gens utilisent-ils des objects fonction appel par valeur? Est-ce que je perds quelque chose en utilisant une référence constante? Ou devrais-je utiliser lvalue-reference? Veuillez noter que c ++ 1y est ok et que l’exemple de code ci-dessus n’est qu’un exemple.

Le type d’argument devrait probablement être funcT const & fun afin que les gros objects ne soient pas copiés,

Ce n’est pas la vue prise par les algorithmes dans les bibliothèques standard. Là, les objects appelables sont pris par valeur. Il appartient à l’auteur de l’object appelé de s’assurer qu’il est raisonnablement bon marché pour le copier. Par exemple, si vous avez besoin d’accéder à quelque chose de grand, vous pouvez demander à l’utilisateur du foncteur de fournir une référence à une référence et de la stocker dans le foncteur – la copie d’une référence est économique.

Maintenant, il se peut que vous souhaitiez faire les choses différemment de la bibliothèque standard, car vous vous attendez à ce que les fonctions de fabrication de particules soient particulièrement difficiles à reproduire ou à déplacer à moindre coût. Mais les programmeurs C ++ sont familiers avec les foncteurs en cours de copie, donc si vous faites ce que la bibliothèque standard fait, vous ne leur tournez généralement pas la vie au pire. Copier les foncteurs n’est pas un problème sauf si vous en faites un 🙂

Il existe plusieurs cas d’utilisation, qui devraient tous être disponibles:

  • Le foncteur n’a pas d’état et est fourni sous forme temporaire: make_particle(MyFun())

  • Le foncteur a un état qui doit être récupéré plus tard: YourFun f; make_particle(f); YourFun f; make_particle(f);

Vous ne pouvez pas résoudre les deux cas avec un seul paramètre de type référence: le premier cas nécessite une référence const lvalue ou une référence rvalue, qui interdit la deuxième utilisation, et le deuxième cas, une référence lvalue, qui interdit la première utilisation.

Un langage courant dans de telles situations est d’accepter le foncteur par valeur et de le renvoyer à la fin:

 template  F map(Iter first, Iter last, F f) { // ... f(*first) ... return f; } 

Cela peut ne pas être tout à fait applicable dans votre cas, cependant, mais c’est une idée. Par exemple, vous pouvez renvoyer un std::pair . Dans tous les cas, vous auriez besoin que votre type de foncteur soit copiable, mais c’est une exigence raisonnable.

Une alternative, soulignée par @Xeo et disponible pour les modèles de fonction uniquement, consiste à prendre l’argument foncteur par référence universelle, ce qui fonctionnera dans les deux cas:

 template  void map(Iter first, Iter last, F && f) { // ... use f(*first) ... } 

Notez que dans ce cas, nous n’utilisons pas std::forward , car nous utilisons f comme une véritable référence, et pas seulement pour le transmettre ailleurs. En particulier, nous ne sums pas autorisés à quitter f si nous prévoyons toujours l’utiliser.