Sign in to follow this  

C++11 lambda, modifying copied value

This topic is 828 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

int fooVar = 10;
 std::function<void()> fooFunction =  [=]() {
 fooVar = 12;
};

Trying to modify foorVar in that lambda results in an error, because its not an lvaue.

 

Why is the copied value made constant?! 

Share this post


Link to post
Share on other sites
Because const data is safer than mutable data. This also makes the lambda more composable. And C++ says so.

As well as to try to prevent people from making the same mistake you've made in your example code - assuming that the change you made in the lambda is visible outside the lambda because the variable has the same name even though it's a completely different value.

What's the point of modifying a value that no one can see? The only one who can read your changed fooVar is the lambda itself, the external fooVar will be untouched because you captured by value.

If you absolutely want the copy to be mutable, you can add the mutable flag to it. But you should generally avoid doing that as other people may not expect a function to return different results when given the same parameters. Edited by SmkViper

Share this post


Link to post
Share on other sites

It makes sense if you want to do something like:

But in your example, there is no reason for counter to exist outside the lambda. It always starts at 10 and always ends at 0. So counter could always exist entirely in the lambda.

[Edit]
Or pass it as a parameter. Since it isn't actually modifying counter, it is modifying a copy of counter.

Share this post


Link to post
Share on other sites

Assuming counter isn't defined as int counter = 10; but is something dynamic. Does your argument still hold?

In a sense, yes. You're not actually manipulating counter, but a copy of counter. The type is irrelevant. It doesn't need to be captured by the lambda, just passed as a parameter. Which for readability would make more sense (in my mind), since that defines it as an input affecting the function.

You probably don't want to be capturing everything by value anyway, as this could be very expensive.

Share this post


Link to post
Share on other sites

If you mark the lambda is mutable, the operator() will not be marked const, so you can modify the fooVar member.


int fooVar = 10;
std::function<void()> fooFunction =  [=]()mutable {
 fooVar = 12; // totally fine now!
};

Keeping in mind, of course, that this happens, because it was copied by value:

fooFunction();
assert(fooVar == 12, "Huh?"); // this fires - function didn't modify this variable

Share this post


Link to post
Share on other sites


If you mark the lambda is mutable, the operator() will not be marked const, so you can modify the fooVar member.

 

But also note that if you do this, you'll need to be very careful about how many times fooFunction is invoked (since the second invocation won't behave identically to the first), as well as if the functor itself it is ever copied, or only ever referenced (and if copied, whether the copy happens before or after any invocations).

Share this post


Link to post
Share on other sites

Also, it's advisable not to store lambdas in std::function objects. It might cost extra memory allocation.

 

The type of each different lambda is known only to the compiler, so use auto instead.

Edited by vdaras

Share this post


Link to post
Share on other sites

Also, it's advisable not to store lambdas in std::function objects. 

 

As you saying, don't store lambdas in std::function unless you need to (i.e. passing a lambda into a function that takes std::function as a parameter), or are you saying never use std::function to store a lambda?

 

I store lambdas in std::function all the time. Ofcourse if the lambda doesn't need to be stored for later calling, I just use auto, but since I still use std::function alot, I'd like to know if there's a problem with that that I'm not aware of.

 

Are you saying that std::function's member vars takes a few extra bytes to hold the object, which requires a separate allocation (definitely worth pointing out), or are you warning about something more dangerous? mellow.png

Share this post


Link to post
Share on other sites

Using std::function for lambdas is like using std::vector for arrays. I tend to use both heavily (except in cases where I don't).

 

The advice is to know what you are doing and to know what it will cost you (but that's true for everything in C++!!).

Share this post


Link to post
Share on other sites

 

Also, it's advisable not to store lambdas in std::function objects. 

 

As you saying, don't store lambdas in std::function unless you need to (i.e. passing a lambda into a function that takes std::function as a parameter), or are you saying never use std::function to store a lambda?

 

I store lambdas in std::function all the time. Ofcourse if the lambda doesn't need to be stored for later calling, I just use auto, but since I still use std::function alot, I'd like to know if there's a problem with that that I'm not aware of.

 

Are you saying that std::function's member vars takes a few extra bytes to hold the object, which requires a separate allocation (definitely worth pointing out), or are you warning about something more dangerous? mellow.png

 

 

You are right I didn't put that correctly. It's totally fine storing them in std::function objects if there is no alternative to the task at hand.

 

The problem with std::function is that its the same size for every kind of function it could store, so it does extra allocations if its memory is not enough to store bookkeeping information about the lambda.

 

Imagine having a game with tons of those small allocations each frame because you just define and store some lambdas. It could hinder performance.

Share this post


Link to post
Share on other sites
std::function is great at what it does - type erasure for a callable.

Like pretty much every other type erasure mechanism, there are downsides - it will allocate if necessary. (In the two library implementations I'm familiar with, std::function can store a lambda with 2-3 captures without allocation)

And like everything else in C++ there are alternatives with other tradeoffs. You can pass a lambda to a function accepting a const template reference, which won't allocate, but of course requires you to put your code in a place the compiler can get at it. You could do the classic function pointer + void data pointer if you don't mind losing type safety.

Or, if you want to be really fancy - an inheriting functor using a template to hold the lambda directly:

class IDelegate
{
public:
  virtual ~IDelegate() {}
  virtual bool operator()() const = 0;
};

namespace Internal
{
  template<typename T>
  class DelegateImpl: public IDelegate
  {
  public:
    DelegateImpl(const T& aFunctor): Functor(aFunctor) {}
    virtual bool operator()() const final {return Functor();}
  private:
    T Functor;
  };

  void DoSomeStuffImpl(const IDelegate& aDelegate); // does the real work
}

template<typename T>
void DoSomeStuff(const T& aFunctor)
{
  DoSomeStuffImpl(Internal::DelegateImpl<T>(aFunctor));
}

Share this post


Link to post
Share on other sites

You are right I didn't put that correctly. It's totally fine storing them in std::function objects if there is no alternative to the task at hand.

The problem with std::function is that its the same size for every kind of function it could store, so it does extra allocations if its memory is not enough to store bookkeeping information about the lambda.

Imagine having a game with tons of those small allocations each frame because you just define and store some lambdas. It could hinder performance.

 

Thanks, wasn't trying to derail the thread, just wasn't sure if I was walking on thin ice with std::function. 

I'm not using millions of lambdas per second, so a small amount of overhead is perfectly fine for me. Thanks for the clarification.

Share this post


Link to post
Share on other sites

In practical terms, all decent implementations of std::function have a small-object optimization. If your stored functor/lambda is small, it won't incur an allocation.

 

You can provide the std::function a custom allocator that fails on all allocations to ensure that you're within your implementation's small-object size, or that it has one at all.

 

The std::function replacement I usually write/use actually enforces the small object requirement, as in it's a compile error to assign a lambda larger than X*sizeof(void*) where X is usually something like 3-4 (in one implementation, I made it a template parameter, so an API could decide if it allowed larger functors, which I happened to need in that project in a handful of cases).

Share this post


Link to post
Share on other sites

This topic is 828 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this