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> m_entities;
public:
template
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
constexpr ControlFlow regularizedInvoke(F&& f, Args&&... a) {
if constexpr(std::is_void_v) {
f(a...);
return ControlFlow::Continue;
} else {
return f(a...);
}
}
template
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