web-dev-qa-db-fra.com

Comment appeler immédiatement un lambda C++?

Un constructeur d'une classe dont j'hérite nécessite la transmission d'un objet non trivial. Semblable à ceci:

MyFoo::MyFoo() : SomeBase( complexstuff )
{
    return;
}

La complexstuff a peu à voir avec MyFoo, je ne voulais donc pas la transmettre.

Au lieu d'écrire une sorte de fonction temporaire à 1 élément qui renvoie complexstuff, j'ai utilisé un lambda. Ce qui m'a pris quelques minutes à comprendre, c'est que je dois invoquer le lambda. Donc, mon code ressemble maintenant à ceci:

MyFoo::MyFoo() : SomeBase(
    []()
    {
        /* blah blah do stuff with complexstuff */
        return complexstuff;
    } () )
{
    return;
}

Si vous ne l'avez pas attrapé, c'est subtil. Mais après le corps lambda, j'ai dû mettre () pour dire au compilateur de "lancer" immédiatement le lambda. Ce qui était logique après avoir compris ce que j'avais mal fait. Sinon, sans le () pour appeler le lambda, gcc dit quelque chose de similaire à ceci:

error: no matching function for call to 'SomeBase(<lambda()>)'

Mais maintenant, cela me fait penser - est-ce que je l'ai fait correctement? Existe-t-il une meilleure façon de dire au compilateur que je souhaite qu'il appelle immédiatement un lambda que j'ai écrit? Ou ajouter un () vide comme je l’ai fait de la manière habituelle?

8
Stéphane

Mais maintenant, cela me fait penser - est-ce que je l'ai fait correctement?

Oui tu peux.

Existe-t-il une meilleure façon de dire au compilateur que je souhaite qu'il appelle immédiatement un lambda que j'ai écrit? 

Pas que je sache de. Un lambda est aussi simplement un objet de fonction, donc vous besoin d'avoir un () pour l'appeler, il n'y a aucun moyen de le contourner (sauf bien sûr, une fonction qui appelle le lambda comme std::invoke).

Si vous le souhaitez, vous pouvez supprimer le () après la liste de capture, car votre lambda ne prend aucun paramètre. 

Ou ajouter un () vide comme je l’ai fait de la manière habituelle?

Oui, c'est le chemin le plus court. Comme indiqué précédemment, std::invoke fonctionnerait également à la place, mais cela nécessiterait davantage de frappe. Je dirais qu'un appel direct avec () est la façon habituelle de procéder.

10
Rakete1111

Il n'y a aucun moyen de dire au compilateur d'appeler immédiatement le lambda. Le cours le plus simple (en termes de complexité et de nombre de caractères saisis) est ce que vous avez déjà fait. C'est aussi très idiomatique pour quiconque a travaillé avec des langues fermées (je pense que JavaScript est ici).

Si vous souhaitez éviter la syntaxe, modifiez SomeBaseou complexstuffpour exécuter l'appelable.


Si tout ce que vous voulez, c'est du sucre syntaxique pour invoquer le lambda, vous pouvez toujours faire quelque chose comme SCOPE_GUARD d'Alexandrescu et abuser de la surcharge de l'opérateur:

Live example

#include <iostream>

constexpr enum {} invoke{};

template<class Callable>
auto operator+(decltype(invoke) const&, Callable c) -> decltype(c()) {
    return c();
}


int main() {
    invoke + []() {
        std::cout << "called";
    };
}

Mais je ne le ferais pas. Inventer votre propre DSL ne fera qu'empirer votre code à maintenir. S'en tenir aux idiomes qui utilisent des constructions en langage simple.

4
StoryTeller

En C++ 17, vous pouvez utiliser std::invoke . Cela fait exactement la même chose que vous, mais peut-être le trouverez-vous plus clair.

#include <iostream>
#include <functional>

void foo(int i)
{
  std::cout << i << '\n';
}

int main()
{
  foo( std::invoke( []() { return 1; } ) );
}
4
Henri Menke

Y a-t-il une meilleure façon 

Vous pouvez également envisager d’avoir une fonction membre privée privée créant la complexstuff, quelque chose comme:

class MyFoo : public Base {
private:
    static SomeComplexType compute_complex_stuff() {
      SomeComplexType complexstuff;
      /*compute the complexstuff */
      return complexstuff;
    };
public: 
    MyFoo() : Base(compute_complex_stuff()) {};
};

Je ne sais pas s'il vaut mieux définir une expression lambda et l'appliquer immédiatement. c'est IMHO une question de goût; pour un short lambda body, je préférerais une expression lambda immédiatement appliquée (mais un compilateur créerait peut-être la fermeture temporaire dans ce cas, de sorte qu'il serait peut-être plus lent sans optimisations; être capable de faire cette optimisation).

En passant, GCC fournit l’extension statement expression language (également comprise par Clang) à vos fins. Avec ça tu pourrais écrire

MyFoo::MyFoo : Base (({
  SomeComplexType complexstuff;
  /*compute the complexstuff */
  return complexstuff;
}) {};
2