← Previous · All Episodes · Next →
Mastering Control Flow in C++ Lambdas: Emulating Break and Continue with Elegance Episode

Mastering Control Flow in C++ Lambdas: Emulating Break and Continue with Elegance

· 02:05

|

In C++, you can’t use break/continue inside a lambda as if it were a real loop. Instead you make your lambda return a signal enum and have the surrounding loop react to it:

enum class ControlFlow {
    Continue,
    Break
};

class EntityStorage {
    std::vector<std::optional<Entity>> m_entities;
public:
    template<typename F>
    void forEntities(F&& f) {
        for (auto& opt : m_entities) {
            if (!opt.has_value()) continue;
            if (f(*opt) == ControlFlow::Break)
                break;
        }
    }
};

Now your lambda returns Continue or Break:

es.forEntities([&](const Entity& e) {
    if (shouldSkip(e))
        return ControlFlow::Continue;   // acts like continue
    if (shouldStop(e))
        return ControlFlow::Break;      // acts like break
    process(e);
    return ControlFlow::Continue;
});

If you’d like to allow a “void” lambda (so you don’t always have to return Continue), wrap calls in a helper:

template<class F, class... Args>
constexpr ControlFlow regularizedInvoke(F&& f, Args&&... a) {
    if constexpr(std::is_void_v<decltype(f(a...))>) {
        f(a...);
        return ControlFlow::Continue;
    } else {
        return f(a...);
    }
}

template<typename F>
void EntityStorage::forEntities(F&& f) {
    for (auto& opt : m_entities) {
        if (!opt.has_value()) continue;
        if (regularizedInvoke(f, *opt) == ControlFlow::Break)
            break;
    }
}

This gives you real break/continue semantics without exposing your internals or writing a full custom iterator.
Link to Article


Subscribe

Listen to jawbreaker.io using one of many popular podcasting apps or directories.

Apple Podcasts Spotify Overcast Pocket Casts Amazon Music
← Previous · All Episodes · Next →