0

In the below contrived example I have a generic function which takes an arbitrary lambda as a callback function, checks some preconditions, and calls the lambda if they are met:

template<ApplyF>
void check_valid(CharacterId id, Operation op, ApplyF apply)
{
    auto it = characters_.find(id);
    if (it != characters_.end())
    {
        Character& character = it->second;
        if (character.operation_valid(op))
        {
            apply(character);
        }
    }
}

This then allows me to reduce boilerplate and only have the lambda invoked when said preconditions are met:

void attack(CharacterId id, CharacterId enemy, double skill, double chance)
{
    check_valid(id, Operation::Attack, [=](Character& character)
                {
                    character.attack(enemy, skill, chance);
                });
};

void harvest(CharacterId id, ResourceId resource, double quantity)
{
    check_valid(id, Operation::Harvest, [=](Character& character)
                {
                    character.harvest(resource, quantity);
                });
};

You can see I am capturing variables required in the lambda by value.

I am also passing the lambda to check_valid by value.

Questions:

Is dynamic memory allocation something I need to worry about?

Could the captured variables and passing the lambda by value result in dynamic memory allocations?

I know lambdas can be inlined, so in this type of usage, would the optimiser see that the capture is only a by-product of language rules, and reference the variable in the outer scope without copying anything at all?

Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
  • I don't understand where dynamic memory allocation even enters the picture. Are you asking whether the captured `CharacterId` for instance will be stored as a `CharacterId *` within the closure type and be dynamically allocated? No, that won't happen. As for the last question, if you want to avoid copying of the captured variable, why are you not capturing them by reference? – Praetorian Jul 10 '18 at 19:14
  • So the lambda and its captured variables will be allocated on the stack? I think perhaps I'm confusing the lambda memory-model with `std::function`, which may well require dynamic allocation? – Steve Lorimer Jul 10 '18 at 19:17
  • Passing lambda by value seldom makes any sense. Are you sure you need it? – SergeyA Jul 10 '18 at 19:19
  • Yes, you're confusing that. You're also generating extra overhead, in any case, by not using forwarding references, so even without dynamic allocation the generated code will likely copy-construct a bunch of temporary objects. – Sam Varshavchik Jul 10 '18 at 19:19
  • The captured variables will become data members of the closure type. If you capture by value, the data members will be value types, else they will be reference types. `std::function` is very different, it's a wrapper that can accept any *Callable* matching a certain signature, and to store arbitrary amounts of state, it must resort to dynamic memory allocation (probably after you exceed some small buffer optimization threshold). – Praetorian Jul 10 '18 at 19:22
  • Thanks all, this has cleared up a lot! – Steve Lorimer Jul 10 '18 at 19:23

0 Answers0