Duration: 0:16:25 (#NoEstimates)
You can download this talk and code samples at
https://github.com/daixtrose/crazy-generic-lambdas
In order to make it easy for you to follow this talk and for everyone who is visually impaired I share the code for this talk on my GitHub account.
You can download the code and the utilities I present here under permissive licences. The talk and code are distributed under the MIT license. The fotos that I made and use as illustrations are available under the Creative Commons Attribution-ShareAlike.
The code that I have stolen and reused here is also under MIT license. So everyone should be fine.
Lambdas are syntactic sugar
for function objects
#include <iostream>struct C { int operator()() { return 42; } };int main(){ C c; // eagerly waiting for std::print std::cout << c() << "\n";}
Lambdas are syntactic sugar for function objects
#include <iostream>int main(){ auto c = []() { return 42; }; // eagerly waiting for std::print std::cout << c() << "\n";}
Check on https://cppinsights.io/ decompiler
auto c = []() { return 42; };
class __lambda_1_11{ public: inline int operator()() const { return 42; } // ... some function pointer stuff omitted ... // __lambda_1_11() = default;}; __lambda_1_11 c = __lambda_1_11{};
auto c = [](int i) { auto result = [=] { return i; }; return result;};static_assert(42 == c(42)());
class __lambda_1_14 { public: inline __lambda_2_23 operator()(int i) const { class __lambda_2_23 { private: int i; public: __lambda_2_23(int & _i) : i{_i} {} inline int operator()() const { return i; } }; __lambda_2_23 result = __lambda_2_23{i} return __lambda_2_23(result); }__lambda_1_14 c = __lambda_1_14{};
Lambdas can be marked as can be evaluated at compile time via
constexpr
int main(){ auto c = []() constexpr { return 42; }; static_assert(42 == c());}
A lambda is implicitly constexpr if its result satisfies the requirements of a constexpr function:
int main(){ /* implicitly constexpr */ auto c = []() /* implicitly constexpr */ { return 42; }; // Won't compile, no reassignment possible: // c = []() { return 43; }; static_assert(42 == c());}
Lambdas can be marked as must evaluate at compile time via
consteval
, but since they are implicitly constexpr, consteval is not always necessary.
int main(){ auto c = [](int i) consteval { return i + 1; }; static_assert(42 == c(41));}
Lambdas can be generic (as I promised)
#include <type_traits>int main(){ auto c = []<typename T>(T i) { static_assert( std::is_integral_v<T>); return i + 1; }; static_assert(42 == c(41));}
#include <type_traits>int main(){ auto c = [](auto i) { static_assert( std::is_integral_v< decltype(i)>); return i + 1; }; static_assert(42 == c(41));}
#include <type_traits>int main(){ auto c = []<typename T>(T i) { return i + 1; }; // error: // 'c' does not name a template but is followed by template arguments // static_assert(42 == c<int>(41)); static_assert(42 == c.template operator()<int>(41));}
#include <type_traits>int main(){ auto c = []<int I>() { return I + 1; }; static_assert(42 == c.template operator()<41>()); // ^^^^^^^^^^^^^^^^^^^^^^^}
#include <type_traits>// a helper function template <int I> consteval auto get(auto const & c) { return c.template operator()<I>();}int main(){ auto c = []<int I>() { return I + 1; }; static_assert(42 == get<41>(c));}
template<int I> consteval auto get(auto c) { return c.template operator()<I>();}int main(){ constexpr auto c = []<int I>() {}; // start empty constexpr auto c_ = addItem<1>(c, "Hello"); // add an item constexpr auto c__ = addItem<2>(c_, 42); // add another one std::cout << get<1>(c__) << " "; // get the 1st value back std::cout << get<2>(c__) << "\n"; // get the 2nd value back }
addItem
template<int I>consteval auto addItem(auto origin, auto value) { auto result = [=]<int J>() constexpr { if constexpr(I == J) { return value; } else { return origin.template operator()<J>(); } }; return result;}
template<int I>consteval auto addItem(auto origin, auto value) { // _return_ a lambda that _returns_ value ... auto result = [=]<int J>() constexpr { // ... if (and only if) called with I if constexpr(I == J) { return value; } else { return origin.template operator()<J>(); } }; return result;}
template<int I>consteval auto addItem(auto origin, auto value) { auto result = [=]<int J>() constexpr { if constexpr(I == J) { return value; } else { return origin.template operator()<J>(); } }; return result;}
constexpr auto c = []<int I>() {}; // start empty // | // \----------------| // V constexpr auto c_ = addItem<1>(c, "Hello"); // add an item // | // \-----------------| // V constexpr auto c__ = addItem<2>(c_, 42); // add another one std::cout << get<1>(c__) << " "; // get the 1st value back std::cout << get<2>(c__) << "\n"; // get the 2nd value back // std::cout << get<3>(c__) << "\n"; // compiler error (void)
template<int I> consteval auto get(auto c) { return c.template operator()<I>();}int main(){ constexpr auto c = []<int I>() {}; // start empty constexpr auto c_ = addItem<1>(c, "Hello"); // add an item constexpr auto c__ = addItem<1>(c_, "World"); // <--- should fail! }
template<int I>consteval auto addItem(auto origin, auto value) requires (std::is_void_v<decltype(origin.template operator()<I>())>) { auto result = [=]<int J>() constexpr { if constexpr(I == J) { return value; } else { return origin.template operator()<J>(); } }; return result;}
https://godbolt.org/z/5f168eofh
[[maybe_unused]] auto fi = [](int i) { return i * i; };[[maybe_unused]] auto fd = [](double d) { return 2.0 * d; };struct overload : decltype(fi), decltype(fd) { using decltype(fi)::operator(); using decltype(fd)::operator(); };int main() { overload o; // We can ... o(42); // Country ... o(42.0); // and Western!}
auto fi = [](int i) { return i * i; };auto fd = [](double d) { return 2.0 * d; };template <typename F1, typename F2> struct overload : F1, F2 { using F1::operator(); using F2::operator(); };auto make_overload(auto f1, auto f2) { return overload<decltype(f1), decltype(f2)>{}; }int main() { auto o = make_overload(fi, fd); // We can ... o(42); // Country ... o(42.0); // and Western!}
See https://arne-mertz.de/2018/05/overload-build-a-variant-visitor-on-the-fly/
template <class ...Fs>struct overload : Fs... { template <class ...Ts> overload(Ts&& ...ts) : Fs{std::forward<Ts>(ts)}... {} using Fs::operator()...;};template <class ...Ts>overload(Ts&&...) -> overload<std::remove_reference_t<Ts>...>;
Thank You for Your Attention
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |