8

I'm having trouble knowing when to pass/store std::function objects by value or reference, or if I need to use move semantics somehow. I have a struct that stores two std::function objects:

struct Control{
    char key;
    std::function<void()> press;
    std::function<void()> release;
    Control(char key, std::function<void()> press, std::function<void()> release):
        key(key),press(press),release(release){}
}

I also have a class that contains a vector of these structs, and I'd like to initialize it in a function resembling this one:

void Screen::init(Player& player){

    this->controls.push_back(Control(
        'W',
        [&player](){player.go(UP);},
        [&player](){player.stop(UP);}));

}

I'd like to be able to pass lambda expressions directly to the Control constructor, and eventually do this repeatedly:

void Screen::update(){
    foreach (auto control : controls){
        if(...) 
            control.press();
        else if (...) 
            control.release();
    }
}

I've encountered a lot of errors and crashes trying to implement this idea, so I need to know

  • Should the std::function's be stored by (const?) reference, or by value, taking into account they capture a reference?
  • Should they be passed to the Control constructor by (const?) reference, or value, (or moved, somehow)?
  • Is it alright to store the Controls by value in a vector this way, or will I need to use an alternative (unique_ptr<Control>, etc.)?
  • when I loop through the controls vector, should I be accessing them by value, or by reference?

Assume that the Player& object is always in scope for Screen::update().

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Eric B
  • 4,367
  • 5
  • 33
  • 43
  • If you pass/save everything by value, it should work. http://ideone.com/Z07KQs – Lol4t0 Jun 07 '13 at 18:57
  • Wow, I guess I was really overthinking this. Thanks for the code sample! Some analysis as to *why* would be nice though. I was mostly concerned about the lambda captures and how that might interfere, but I guess it isn't an issue. – Eric B Jun 07 '13 at 19:00
  • 1
    lambdas and functions encapsulates all the stuff, so you can feel free to copy lambda and `std::function`. _That is the point_. The only point is that `Player` object should be alive all the time lambda exists. But you know it is already. – Lol4t0 Jun 07 '13 at 19:23
  • 5
    *Nothing* should be *stored* by `const&`. Not unless you *really* know what you're doing. – Nicol Bolas Jun 07 '13 at 19:34
  • Storing references, const or not, is indeed dangerous as you can easily end up with dangling references. Looking at Control's constructor though, you could pass the functions by reference-to-const in order to avoid unnecessary copying. These std::functions are not cheap to copy, because they have to carry possibly bound parameters, which requires dynamic allocation (though it can be optimized using "short string optimization"). – Ulrich Eckhardt Jun 08 '13 at 09:33

1 Answers1

1

Should the std::functions be stored by (const?) reference, or by value, taking into account they capture a reference?

Lambdas should be stored by value. They are small and easily copied.

The only potential issue to check for when copying is if any of the captured values will not exists or are not themselves copy-able. More info on this here.

In your case Player will still be around and copying a reference is fine.

Should they be passed to the Control constructor by (const?) reference, or value, (or moved, somehow)?

Pass lamdas by value for the same reasons as above.

Is it alright to store the Controls by value in a vector this way

Yes.

When I loop through the controls vector, should I be accessing them by value, or by reference

Either. Although Control is such a small class I don't see any reason not to access by value.