Lambda作为基类

和Lambdas一起玩,我发现了一个我不完全明白的有趣的行为。

Supose我有一个从2个模板参数派生的struct Overload ,并有一个using F1::operator(); 条款。

现在,如果我从两个函数派生,我只能访问F1的operator()(正如我所期望的)

如果我从两个Lambda函数派生出来,这不再是事实:我也可以从F2访问operator()。

 #include <iostream> // I compiled 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 <typename F1, typename F2> struct Overload : public F1, public F2 { Overload() : F1() , F2() {} Overload(F1 x1, F2 x2) : F1(x1) , F2(x2) {} using F1::operator(); }; template <typename F1, typename F2> auto get(F1 x1, F2 x2) -> Overload<F1, F2> { return Overload<F1, F2>(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; } 

该标准规定:

lambdaexpression式(也是闭包对象的types)的types是唯一的,未命名的非联合类types

所以每个Lambda的types都应该是唯一的。

我无法解释为什么会这样:请问有谁能提供一些启示?

除了operator() ,由lambda定义的类可以(在正确的情况下)提供对指向函数的指针的转换。 这种情况(或者至less是主要的)是lambda不能捕获任何东西。

如果你添加一个捕获:

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

…不再提供pointer to function的转换,所以试图编译上面的代码给出了您一直期待的错误:

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

一个lambda生成一个函子类。

事实上,你可以从lambda中派生出多态lambda!

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

打印

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

怎么样?

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

当然…这仍然隐藏着魔法。 它是从所有的lambdas中“神奇地”派生出来的Overload类,并公开相应的operator()

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

看到它住在Coliru