Lambda funkcjonuje jako klasy bazowe
Bawiąc się Lambdami znalazłem ciekawe zachowanie, którego nie do końca rozumiem.
Supose I have a struct Overload
that derived from 2 template parameters, and has a using F1::operator();
clause.
Teraz, jeśli wywodzę się z dwóch funktorów, mogę uzyskać dostęp tylko do operatora () F1 (jak bym się spodziewał)
Jeśli wywodzę się z dwóch funkcji Lambda, nie jest to już prawdą: mogę również uzyskać dostęp do operatora () z F2.
#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;
}
Norma stwierdza, że:
Więc każdy rodzaj Lambdy powinien być unikalny.Typ lambda-wyrażenie (które jest również typem obiektu zamknięcia) jest unikalnym, nienazwanym typem klasy non-union
Nie potrafię wyjaśnić, dlaczego tak jest: czy ktoś może rzucić na to trochę światła?
2 answers
Oprócz operator()
, klasa zdefiniowana przez lambda może (w odpowiednich okolicznościach) zapewnić konwersję na wskaźnik do funkcji. Okoliczność (a przynajmniej pierwotna) jest taka, że lambda nie potrafi niczego uchwycić.
Jeśli dodasz ujęcie:
auto f1 = get(
[]() { std::cout << "lambda1::operator()()\n"; },
[i](int) { std::cout << "lambda2::operator()(int)\n"; }
);
f1();
f1(2);
...konwersja do pointer to function
nie jest już dostępna, więc próba skompilowania powyższego kodu daje błąd, którego prawdopodobnie oczekiwałeś od samego początku:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-25 19:01:49
Lambda generuje klasę functora.
Rzeczywiście, można czerpać z lambda i mieć polimorficzne 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";
}
Druki
[42]
[yay for c++11]
[void]
Jak?
template <typename... Fs>
Overload<Fs...> make_overload(Fs&&... fs)
{
return { std::forward<Fs>(fs)... };
}
Oczywiście... to wciąż ukrywa magię. Jest to klasa Overload
, która "magicznie" wywodzi się ze wszystkich lambd i eksponuje odpowiadające im 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)... };
}
Zobacz też Live on Coliru
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-11 22:19:11