9

How would I catch a member variable by value when using C++11 lambda expressions?

Using the [my_member] syntax doesn't seem to work, and implicit capture uses the this pointer. What is need is a way to explicitly specify capture type of member variables. Is that possible?

My workaround for now is:

void member_function()
{
    std::shared_ptr<my_member_class> my_member_copy = my_member; // this shouldn't be necessary
    std::async([=]{ std::cout << *my_member_copy; });
    // std::async([=]{ std::cout << *my_member_; }); // wrong, my member could be potentially out of scope
}
Xeo
  • 129,499
  • 52
  • 291
  • 397
ronag
  • 49,529
  • 25
  • 126
  • 221

5 Answers5

7

I don't think you can capture a member by value, you can capture this but since the member is part of this you'll be using a shared member and not a new variable.

Not knowing what type your member is something like this should work:

auto copy = my_member;
std::async([copy]{ std::cout << copy; });

I don't understand why you're using a shared_ptr in your example, if you want to capture by value surely shared_ptr is the last thing you should consider.

Motti
  • 110,860
  • 49
  • 189
  • 262
  • I need to capture by value inorder for the shared_ptr ref count to be incremented. Otherwise the object will be destroyed when the parent object is destroyed, thus causing a potential access violation in the async operation. (forgot to add * in the lambda). I don't see how you example differs from mine. – ronag Sep 15 '10 at 14:41
  • "Not knowing what type your member is something like this should work:", the reason i wrote std::shared_ptr instead of auto was to be explicit about the type of "my_member", that is shared_ptr. – ronag Sep 15 '10 at 15:39
5

Unfortunately, I don't think there is a straight-forward way to do this, but I can think of a couple of ways to capture a member without making an extra copy.

The first option is similar to your example but uses a reference for the local variable:

void member_function()
{
   std::shared_ptr<my_member_class> &my_member_ref = my_member;
   // Copied by the lambda capture
   std::async([my_member_ref]{ std::cout << *my_member_ref; });
}

Note that there is a bug in pre 4.6.2 versions of GCC that cause the value not to be copied. See Capturing reference variable by copy in C++0x lambda.

A second approach would be to use bind to make the copy:

void member_function()
{
   // Copied by std::bind
   std::async(std::bind([](const shared_ptr<my_member_class>& my_member){
      std::cout << *my_member; }, my_member));
}

In this example, bind will make its own copy of my_member, and this copy will then be passed to the lambda expression by reference.

Community
  • 1
  • 1
rkjnsn
  • 913
  • 7
  • 16
  • Neither of your examples differ from my example. – ronag Mar 02 '12 at 12:46
  • 3
    @ronag The key difference between my examples and your workaround is that my examples don't make an extra copy of `my_member`. My first example merely binds the member to a local reference, allowing it to be captured by value in the lambda capture without being copied first. My second example was a way to avoid the need to use a local variable all together, using bind to capture `my_member` by value instead of the lambda capture. – rkjnsn Mar 02 '12 at 22:23
  • @ronag: rkjnsn is right, his example saves the increment/decrement/pointer-copy when constructing `my_member_copy` and should be thus faster. Why would you downvote his answer? – marton78 Nov 12 '12 at 13:23
  • @marton78: I can't imagine any modern compiler that wouldn't remove the unnecessary copy. – ronag Nov 12 '12 at 14:54
  • @ronag: Are you sure? `shared_ptr` is not so simple, its copy constructor has the side-effect of incrementing a counter in a thread-safe manner, which is implemented on most (all?) platforms with atomics. I wouldn't bet that can be optimized away. – marton78 Nov 13 '12 at 08:51
  • @marton78: Yes I am sure. The compiler is allowed to perform copy elision on any copy assignment/construction operation, regardless of its implementation, e.g. even if you have a printf statement (or some other side-effect) in the copy implementation the compiler will perform copy elision. – ronag Nov 13 '12 at 09:44
  • @marton78: See §12.8 .31 in n3337: "When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, **even if the copy/move constructor and/or destructor for the object have side effects**. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization." – ronag Nov 13 '12 at 09:51
  • 1
    Yes, I know about copy elision, but its criteria are not met in this case, since the sources of both copies are not temporaries: 1) `my_member` is not a temporary when constructing `my_member_copy` 2) `my_member_copy` is not a temporary when constructing the lambda. – marton78 Nov 13 '12 at 11:19
4

Since your question is about C++11 this is not really an answer, but in C++14 you can do like this:

void member_function()
{
    std::async([my_member=my_member]{ std::cout << *my_member; });
}

It does the same thing as your own "work-around" (if my_member is a shared_ptr).

1
auto& copy = my_member;
std::async([copy]{ std::cout << copy; });

auto& (above) also works and obviates copying twice. Although this approach is more syntax than passing [this], it obviates passing into the closure a dependency on the object [this] points to.

c4augustus
  • 176
  • 2
  • 6
  • 1
    This actually works and I double checked it does actually capture by copying and not by reference. Thanks! – Timmmm Mar 23 '18 at 15:40
-1

Right now, I faced the same problem and solved it myself:

  1. Capture the this pointer.
  2. then write this->member syntax inside the lambda:

That is,

 std::async([this]{ std::cout << this->my_member_; } ); 
 //          ^^^^                ^^^^^^ use this syntax
 //           | 
 //           ^
 //           capture `this` as well

It works for me. I hope it should work for you too. However, I'm not completely satisfied with this knowledge. After my work, I'll look for the reason why this syntax is required, or it is a compiler bug. I'm using GCC 4.5.0 (MinGW).

Well, I found the following topic which says this pointer should be captured in order to use the member of the class.

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 4
    But this captures "this" not the actual member variable, so if "this" is deleted then it will cause undefined behavior. – ronag Oct 30 '11 at 16:12