· 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
Listen to jawbreaker.io using one of many popular podcasting apps or directories.