+ - 0:00:00
Notes for current slide
Notes for next slide

Crazy Generic Lambdas

A glimpse on template, auto and those ...

Duration: 0:16:25 (#NoEstimates)

1 / 31

This talk and its accompanying code is Open Source™

You can download this talk and code samples at
https://github.com/daixtrose/crazy-generic-lambdas

 

3 / 31

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.

4 / 31

What are Lambdas?

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";
}


5 / 31

What are Lambdas?

Lambdas are syntactic sugar for function objects

#include <iostream>
int main()
{
auto c = []() {
return 42;
};
// eagerly waiting for std::print
std::cout << c() << "\n";
}


6 / 31

Lambdas are Objects!

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{};
7 / 31

Lambdas Can Store Objects via Binding

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{};
8 / 31
9 / 31

Lambdas and Constexpr

Lambdas can be marked as can be evaluated at compile time via constexpr

int main()
{
auto c = []() constexpr {
return 42;
};
static_assert(42 == c());
}
10 / 31

Lambdas and Constexpr

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());
}
11 / 31

Lambdas and Consteval

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));
}
12 / 31

Generic Lambdas

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));
}
13 / 31

Generic Lambdas: Explicit Parametrization

#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));
}
14 / 31

Generic Lambdas: Explicit Parametrization for Value Parameters

#include <type_traits>
int main()
{
auto c = []<int I>()
{
return I + 1;
};
static_assert(42 == c.template operator()<41>());
// ^^^^^^^^^^^^^^^^^^^^^^^
}
15 / 31

Generic Lambdas: Explicit Parametrization for Value Parameters

#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));
}
16 / 31
17 / 31

Store Multiple Values in a Lambda

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
}
18 / 31

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;
}
19 / 31
20 / 31
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;
}
21 / 31
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;
}
22 / 31

Store Multiple Values in a Lambda

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)
23 / 31

Hinder Overwrites

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!
}
24 / 31
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;
}
25 / 31
26 / 31

You can inherit from the type of a Lambda

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!
}
27 / 31

You can inherit from the type of a Lambda

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!
}
28 / 31

You can inherit from the type of a Lambda

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>...>;
29 / 31

Thank You for Your Attention

30 / 31
31 / 31
Paused

Help

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