Hello, and welcome to C++ Weekly. I am your host,
Jason Turner. I am available for code reviews and on-site training. You know what? On-site training
is a spectacular way to get the members of your team all on the same page about
C++ and the kind of work that you're doing at your organization, so reach out to me
and we can discuss on-site training for your organization. Now this episode is truly going
to be a just for fun episode. This is not going to be a best practices or something that you should
be doing all the time in your code, but just something that makes you think a little bit.
[Music] Of course, before I continue I need to encourage you to check out the
video description so that you can see things like how to participate in the planning of C++
Weekly and how to check out any workshops or books or whatever that I might have available
at this time. Okay. The idea is lambda-only programming, and this came from a student.
So I'm just going to preface this - this really is just for fun, but let's, well, let's do this.
I'm going to create a flat map that is a lambda.
I'm just going to make it a flat map of ints to ints. Now
what I've just done is a generalized capture here
of a newly constructed vector of pairs of ints to ints.
I've used decltype(auto) here, and I hope that the point of that will become clear in a moment.
Okay, so if I find the key, then I'm going to return a reference to it. That's what
the decltype(auto) gives me there. If I get to this line after the for loop,
then I know that I don't have that value in my vector yet.
Now this fails to compile because it says that I'm passing a const vector
to emplace_back, which is true. We need to make this a mutable lambda; and now
it's telling me that I've got inconsistent types because that is an int and a pair of int here,
so I actually need to return 'second' there, and now these should both be int references.
Now what we have created is this flat map which behaves just like a C++ map but is, well, flat.
"lvalue required as left operand of assignment," so we can see we actually didn't get the int
reference that we wanted. Let's do this - instead of decltype(auto) - I'm going to do auto ref and
require that a reference be returned. That should get us working... there we go. Okay,
so now we have a flat map and we can do things like... (Don't we all long for the days where
std::print from C++23 is actually implemented by our compilers?) Okay, very good. We're getting
42 printed from our program. Everything is working as expected - we have a flat map
implemented entirely as a lambda. Well, I told you that things are going to get a little bit
wonky here - let's do this. Let's make a variadic set of inputs. Now this is where things really
are going to get weird because the compiler doesn't know if I've passed in more than one.
I've got this - it's an unexpanded parameter pack, although it's not really giving me that error,
so I need to expand the parameter pack, which, again, is not going to do anything good because
it makes no sense to expand this here even though I am calling it with only one value.
So to get this to actually compile I'm going to have to do a C++17 fold over the comma operator,
so the "last item" is the one that's actually used for the comparison here. All right,
that works. Now why did I go through this? It's so that I can do ridiculous things like this:
Okay, I am doing an indexed operation here. If I passed in one element
because I have this "if constexpr the parameter pack size is one", then do this. Otherwise I am
returning a reference back to the underlying data itself, which means I can do things like this:
Now this is probably a spectacularly bad idea, but there's more to it as well. I could in fact, well,
I could definitely do some weird things here because it is possible
to actually define types in the generalized capture from C++17.
So I'm really not going to take this example any further down this road of ridiculousness,
but I can define a lambda that defines a new type, and so this is a lambda in this case;
or I can immediately invoke it so now I have some new type declared entirely in the capture portion
of my lambda, and I can instantiate it, and then down here inside of my lambda I have now access
to a whole new type and a new object with its own lifetime. I don't know, it was just meant to be
a fun little exploration into ways that you can abuse lambdas. If you really wanted to, you could
go down this entire road and just use lambdas for literally everything. They are that flexible. I'll
be sure to add this to my increasingly growing list of lambda episodes in that lambda playlist.
So thanks for watching this episode of C++ Weekly, I hope you had fun. Be sure to subscribe.