Lambdas

Small functions written right where you need them · 9 min

In one breath

A lambda is a function with no name, written inline, right where it's used, usually to hand to something else ("call this when the timer fires", "sort using this rule"). The [ ] at the start says which outside variables it can use. The big danger: a lambda that runs later can outlive what it captured.

The shape

[ capture ] ( parameters ) { body }

auto Add = [](int32 A, int32 B) { return A + B; };
int32 Five = Add(2, 3);

Blueprint comparison: it's like dragging a red event pin into a Set Timer by Event or Bind Event node and building the logic right there, instead of making a separate named function or custom event somewhere else.

Captures: what the lambda can see

CaptureMeans
[]Nothing from outside
[this]The object you're in, so you can use its members and functions
[Damage]A copy of Damage, taken when the lambda is made
[&Damage]A reference to Damage, the original variable
[=] / [&]Everything used, by copy / by reference. Convenient, but hides what's captured; Epic's style guide prefers listing them

Where you'll meet them in Unreal

1. Finding things in an array

Your FindNativeInputActionByTag, rewritten with FindByPredicate:

const FWarriorInputActionConfig* Found = NativeInputActions.FindByPredicate(
    [&InInputTag](const FWarriorInputActionConfig& Config)
    {
        return Config.InputTag == InInputTag && Config.InputAction;
    });
return Found ? Found->InputAction : nullptr;

The array calls your lambda once per element and returns a pointer to the first one where it returned true, or nullptr. Same result as your loop. Whether it's clearer is a matter of taste. (Found is a pointer into the array, so the TArray rule applies: use it straight away.)

2. Sorting

Enemies.Sort([](const AActor& A, const AActor& B)
{
    return A.GetActorLocation().Z < B.GetActorLocation().Z;   // "A comes before B if..."
});

3. "Do this later": timers, async loads, delegates

FTimerHandle Handle;
GetWorldTimerManager().SetTimer(Handle, [this]()
{
    Debug::Print(TEXT("Two seconds later"));
}, 2.0f, false);

Or, from the startup data lesson, the async version of LoadSynchronous: "load this in the background, and run this lambda when it's done."

The trap: lambdas that run later

A lambda that runs right now (FindByPredicate, Sort) is safe to capture anything. A lambda that runs later (timer, async load, delegate) may run after what it captured is gone:

The safe pattern: capture a weak pointer and check it when the lambda runs:

TWeakObjectPtr<ThisClass> WeakThis(this);
StartAsyncLoad([WeakThis]()
{
    if (!WeakThis.IsValid()) return;   // we were destroyed while loading: do nothing
    WeakThis->OnLoaded();
});

And capture plain values (int32, FName) by copy when the lambda runs later.

When not to use one

Quiz yourself

What is a lambda, in one sentence?

An unnamed function written inline where it's used, usually passed to something else to call.

What do the three bracket types in [ ]( ){ } hold?

[ ] captures (outside variables it can use), ( ) parameters, { } the body.

[Damage] vs [&Damage]?

A copy taken when the lambda is created, vs a reference to the original variable (which sees later changes, and dangles if the variable dies).

Why is auto used to store a lambda?

Each lambda has its own unique unnamed type that you can't write out.

Why is [this] risky in an async-load callback but fine in Sort?

Sort runs the lambda immediately while the object definitely exists. An async callback runs later, possibly after the object is destroyed, so this dangles.

What's the safe way to refer to the owning object in a delayed lambda?

Capture a TWeakObjectPtr to it, and check IsValid() at the start of the lambda.

What does FindByPredicate return?

A pointer to the first element for which the lambda returns true, or nullptr.

Why can't you bind a lambda to OnComponentBeginOverlap?

It's a dynamic delegate (usable from Blueprint), which binds through reflection with AddDynamic and needs a named UFUNCTION. A lambda has no name and can't be a UFUNCTION.

Blueprint equivalent of a lambda?

Roughly: wiring an event pin straight into a node like Set Timer by Event or Bind Event and building the logic there, instead of a separate named function.