Modern (Effective) C++ - Avoid default capture modes

The default capture modes in C++ lambdas are -
  • Capture by reference  [&]
  • Capture by value [=]
Default capture by references can end up with dangling references. When we use [&], the closure captures the local variables in the scope where it is defined in. If the closure outlives the function (or the scope it is defined in), it will lead to dangling references.This usually happens when -
We store the lambda (which captures one or more local variables by reference) for later usage
Return the lambda (which captures one or more local variables by reference) from the current function.


In the below example -


The adder closure captures the local variable x and it is returned from function getRandomAdderFunction. Once in main, variable x no longer exists. Hence we get a garbage/ undefined output.

The issue is also seen during capture by value in some special cases. They are -
Copying raw pointers  - First you shouldn't be using raw pointers. But still if you insist on using raw pointers, and when you capture it in your closure, it's a problem waiting to happen. After you capture the pointer, and store the closure for later use, you would want to delete your pointer like a responsible programmer. But check out the output if do that -


Hence you should use std::shared_ptr in such cases.

Capturing members of the class - When you use [=] in a lambda, you capture only and all local variables by value  i.e copy ( not statics, not globals etc). When you create a closure in a member function, one of those local variables is the this pointer. And this is a raw pointer. We already saw what happens when you capture a raw pointer in the above case.  You should also take a special note that member variables are NOT local variables. The below is the right way to capture member variables(line 17).



Static variables -.To recap again, captures only work on local variables. If you have a static variable in your scope, it is captured by reference by default, even though you use [=]. You should use the format similar capturing member variables of class, when you capture static. The program below demonstrates the wrong way(line 10), as well as the right way(line 18).


In the nutshell, lambda functions should be as self contained as much as possible, in order to avoid dangling references and pointers, side effects and other problems, especially if you intend to store the closure for later or repeated use. The precautions explained in this chapter is not necessary if you use the lambda function immediately, for example, when using anonymous lambdas as predicates and such.

The post is inspired by Item 31 of Effective Moden C++ by Scott Meyers.  I highly recommend you guys to check it out.
.

Modern (Effective) C++ - Avoid default capture modes Modern (Effective) C++ - Avoid default capture modes Reviewed by zeroingTheDot on March 17, 2018 Rating: 5

1 comment:

  1. Thank you . This is very useful Avinash.
    In lamda if we de-reference a deleted pointer, will its value always be 0(NULL) ?

    ReplyDelete

Powered by Blogger.