Fonctions Lambda en tant que classes de base

En jouant avec Lambdas, j’ai trouvé un comportement intéressant que je ne comprends pas bien.

Supposons que j’ai une struct Overload qui dérive de 2 parameters de modèle, et using F1::operator(); un using F1::operator(); clause.

Maintenant, si je dérive de deux foncteurs, je ne peux accéder qu’à l’opérateur () de F1 (comme je l’espérais)

Si je dérive de deux fonctions Lambda, ce n’est plus vrai: je peux également accéder à l’opérateur () depuis F2.

 #include  // I comstackd with g++ (GCC) 4.7.2 20121109 (Red Hat 4.7.2-8) // // g++ -Wall -std=c++11 -g main.cc // g++ -Wall -std=c++11 -DFUNCTOR -g main.cc // // or clang clang version 3.3 (tags/RELEASE_33/rc2) // // clang++ -Wall -std=c++11 -g main.cc // clang++ -Wall -std=c++11 -DFUNCTOR -g main.cc // // on a Linux localhost.localdomain 3.9.6-200.fc18.i686 #1 SMP Thu Jun 13 // 19:29:40 UTC 2013 i686 i686 i386 GNU/Linux box struct Functor1 { void operator()() { std::cout << "Functor1::operator()()\n"; } }; struct Functor2 { void operator()(int) { std::cout << "Functor2::operator()(int)\n"; } }; template  struct Overload : public F1, public F2 { Overload() : F1() , F2() {} Overload(F1 x1, F2 x2) : F1(x1) , F2(x2) {} using F1::operator(); }; template  auto get(F1 x1, F2 x2) -> Overload { return Overload(x1, x2); } int main(int argc, char *argv[]) { auto f = get(Functor1(), Functor2()); f(); #ifdef FUNCTOR f(2); // this one doesn't work IMHO correctly #endif auto f1 = get( []() { std::cout << "lambda1::operator()()\n"; }, [](int) { std::cout << "lambda2::operator()(int)\n"; } ); f1(); f1(2); // this one works but I don't know why return 0; } 

La norme stipule que:

Le type de l’expression lambda (qui est également le type de l’object de fermeture) est un type de classe sans union unique unique, non nommé

Ainsi, chaque type de Lambda doit être unique.

Je ne peux pas expliquer pourquoi il en est ainsi: quelqu’un peut-il nous éclairer, s’il vous plaît?

En plus de operator() , une classe définie par un lambda peut (dans certaines circonstances) permettre une conversion en un pointeur vers la fonction. La circonstance (ou du moins la principale) est que le lambda ne peut rien capturer.

Si vous ajoutez une capture:

 auto f1 = get( []() { std::cout << "lambda1::operator()()\n"; }, [i](int) { std::cout << "lambda2::operator()(int)\n"; } ); f1(); f1(2); 

... la conversion en pointer to function n'est plus fournie, alors essayer de comstackr le code ci-dessus donne l'erreur que vous attendiez probablement tout au long:

 trash9.cpp: In function 'int main(int, char**)': trash9.cpp:49:9: error: no match for call to '(Overload, main(int, char**):: >) (int)' trash9.cpp:14:8: note: candidate is: trash9.cpp:45:23: note: main(int, char**):: trash9.cpp:45:23: note: candidate expects 0 arguments, 1 provided 

Un lambda génère une classe de foncteur.

En effet, vous pouvez dériver de lambda et avoir des lambda polymorphes!

 #include  #include  int main() { auto overload = make_overload( [](int i) { return '[' + std::to_ssortingng(i) + ']'; }, [](std::ssortingng s) { return '[' + s + ']'; }, [] { return "[void]"; } ); std::cout << overload(42) << "\n"; std::cout << overload("yay for c++11") << "\n"; std::cout << overload() << "\n"; } 

Des tirages

 [42] [yay for c++11] [void] 

Comment?

 template  Overload make_overload(Fs&&... fs) { return { std::forward(fs)... }; } 

Bien sûr ... cela cache encore la magie. C'est la classe Overload qui dérive "comme par magie" de tous les lambdas et expose l' operator() correspondant operator() :

 #include  template  struct Overload; template  struct Overload { Overload(F&& f) : _f(std::forward(f)) { } template  auto operator()(Args&&... args) const -> decltype(std::declval()(std::forward(args)...)) { return _f(std::forward(args)...); } private: F _f; }; template  struct Overload : Overload, Overload { using Overload::operator(); using Overload::operator(); Overload(F&& f, Fs&&... fs) : Overload(std::forward(f)), Overload(std::forward(fs)...) { } }; template  Overload make_overload(Fs&&... fs) { return { std::forward(fs)... }; } 

Voir le live sur Coliru