3

I'd like to use lambda functions to asynchronously call a method on a reference counted object:

void RunAsync(const std::function<void()>& f) { /* ... */ }
SmartPtr<T> objPtr = ...
RunAsync([objPtr] { objPtr->Method(); });

Creating the lambda expression obviously creates a copy but I now have the problem that converting the lambda expression to a std::function object also creates a bunch of copies of my smart pointer and each copy increases the reference count.

The following code should demonstrate this behavior:

#include <functional>
struct C {
 C() {}
 C(const C& c) { ++s_copies; }
 void CallMe() const {}
 static int s_copies;
};
int C::s_copies = 0;
void Apply(const std::function<void()>& fct) { fct(); }
int main() {
 C c;
 std::function<void()> f0 = [c] { c.CallMe(); };
 Apply(f0);
 // s_copies = 4
}

While the amount of references goes back to normal afterwards, I'd like to prevent too many referencing operations for performance reasons. I'm not sure where all these copy operations come from.

Is there any way to achieve this with less copies of my smart pointer object?

Update: Compiler is Visual Studio 2010.

asked Nov 28, 2011 at 16:16
10
  • 4
    Why don't you capture by reference? As in [&c] { c.CallMe(); }; and same with smart pointer. ` Commented Nov 28, 2011 at 16:25
  • 2
    @Dani: In many reference-counted smart pointers, the arithmetic is atomic, and thus requires more overhead. Commented Nov 28, 2011 at 16:27
  • 1
    @Nawaz Hm, I wasn't sure if that was possible. What happens when the variable goes out of scope if it is only captured by reference? Commented Nov 28, 2011 at 16:28
  • 2
    Have you considered using a function template so that you can eschew std::function altogether? If you can make your lambda captureless (i.e., if you can find some other way to get C into the lambda, e.g. by using a parameter), you can use a function pointer. Commented Nov 28, 2011 at 16:29
  • 2
    Which compiler are you using? MSVC2010 does not property implement default move constructors, so your lambda is only copyable. I suspect if you manually implemented the lambda, with move semantics, you'll see what you want (which is how a proper implementation of C++11 would act). Commented Nov 28, 2011 at 18:17

2 Answers 2

5

std::function probably won't be as fast as a custom functor until compilers implement some serious special treatment of the simple cases.

But the reference-counting problem is symptomatic of copying when move is appropriate. As others have noted in the comments, MSVC doesn't properly implement move. The usage you've described requires only moving, not copying, so the reference count should never be touched.

If you can, try compiling with GCC and see if the issue goes away.

answered Nov 28, 2011 at 20:04
Sign up to request clarification or add additional context in comments.

3 Comments

Unfortunately, I cannot compile the whole project in GCC but my demo code behaves as expected in GCC. So if I understand correctly the issue isn't the lambda expression but the std::function implementation? It appears as if I have the same (or worse) issue when using std::function with std::bind.
Also, I used to have a custom functor implementation that I wanted to replace by std::function that doesn't show this behavior. Seems I can't get rid of it yet.
@fschoenm I don't use Windows, but James McNellis works at Microsoft, so you might try his suggestion in the comments. Try replacing the std::function with a raw function pointer, since lambda functions convert to that too.
2

Converting to a std::function should only make a move of the lambda. If this isn't what's done, then there's arguably a bug in the implementation or specification of std::function. In addition, in your above code, I can only see two copies of the original c, one to create the lambda and another to create the std::function from it. I don't see where the extra copy is coming from.

answered Nov 28, 2011 at 20:12

1 Comment

I forgot to mention that I'm using Visual Studio 2010. In GCC I also get only two copies (one if I implement a move constructor).

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.