8

While std::function is movable, in some cases it's not possible or not convenient. Is there a significant penalty for copying it?

Does it possibly depend on the size of the captured variables (if it was created using a lambda expression)? Is it implementation dependent?

Petr
  • 62,528
  • 13
  • 153
  • 317
  • obviously if your function object contains captured objects the copying penalty will be proportional to the amount of captured objects. – David Haim Jun 06 '17 at 11:32
  • and as a side note, as `std::function` must copy the given target, it is not the best object if performance is what you like – David Haim Jun 06 '17 at 11:33
  • 2
    Measure it. Even if it is supposed to be implementation-independent (which it isn't), it would still depend on quality-of-implementation, and you'd need to measure. Even if it depends on the captured variables, you won't know _how_ it varies with their type/size/number unless you measure. – Useless Jun 06 '17 at 11:35

2 Answers2

19

std::function is typically implemented as a value-semantics, small-buffer optimized, virtual-dispatch, type-erasing class.

This means if your state is small, a copy will involve no heap allocation (other than within the copy ctor of the state) and some indirection (to find how to copy this particular state).

If your state is large (larger than two std::strings on current MSVC, for example), it requires an additional heap allocation to store the state.

This is not something you want to do on a per-pixel per-frame basis, but it isn't insanely expensive.

How your particular compiler and library version implements std::function could vary, but I am unaware of any that differs significantly from the above.

So, in the following code:

std::function<std::string()> f = [s = "hello"s]{ return s; };

copying f will involve 0 heap allocations on MSVC.

Whereas, this code:

std::function<std::string()> g = [a = "a"s, b = "b"s, c = "c"s]{ return a+b+c; };

does require a heap allocation to copy g.

(And yes, both of these are really dumb function objects.)

There is an effective requirement that the small buffer optimization (SBO) be applied for certain cases (function pointers, member function pointers) in std::function by the fact that they are expected not to fail at allocating memory. Once you have written one SBO case, making it more general isn't hard, so most std::function implementations store small objects "inside themselves" and larger objects on the heap.

However, this threshold is not specified by the standard, and it is important for the performance cost, so if performance is really important, you'll have to profile to ensure that your implementation does it.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
7

It is indeed implementation-dependent. It also depends on what exactly you are storing in the std::function. It is surely not as cheap as simply copying a C-style function pointer.

The best thing would be for you to try writing the code the way you find clear and convenient, then, if it does not run quickly enough, profile it. If you need ultimate performance, you may find std::function is not suitable at all.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • 2
    Often there're multiple "clear and convenient" ways (to some extend). Using or not `std::function` can mean high-level design decision that is not easy to reverse on later stages. Understanding pricing is important. IMHO @Yakk's answer is much more helpful. – Andriy Tylychko Jun 06 '17 at 15:22