Serial Lazer
Serial Lazer

Reputation: 1669

Temporary captured-variables in lambda functions - C++11

I was trying out something like this to pre-populate a map using a vector-list of strings. The code is self-explanatory:

Constructor(const vector<string>& names) {
  for_each(names.begin(), names.end(),
                     [this, counter = 1](const String& choice) mutable {
                            nameMapping.emplace(choice, counter++);
                        }
  );
}

Something I didnt really understand is how does counter work?

FYI: counter is no-where declared outside of the lambda function.

But yet, I am able to create a local-variable in class-scope and modify it in a mutable lambda fn?

Can someone please help me understand whats going on.

Upvotes: 4

Views: 1275

Answers (2)

Asteroids With Wings
Asteroids With Wings

Reputation: 17474

But yet, I am able to create a local-variable in class-scope and modify it in a mutable lambda fn?

Can someone please help me understand whats going on.

It's exactly as you said.

Possibly confusing because there's no type given in this particular kind of declaration. Personally I think that was an awful design decision, but there we go.

Imagine it says auto counter = 1 instead; the auto is done for you. The variable then becomes a "member" of the lambda object, giving it state.

The code's not great, because the lambda isn't guaranteed to be applied to the container elements in order. A simple for loop would arguably be much simpler, clearer and predictable:

Constructor(const vector<string>& names)
{
   int counter = 1;
   for (const string& name : names)
      nameMapping.emplace(name, counter++);
}

There's really no reason to complicate matters just for the sake of using "fancy" standard algorithms.

Upvotes: 0

Alex Shirley
Alex Shirley

Reputation: 425

When you set counter = 1 you're declaring a new temporary counter equal to 1. The compiler does the work of determining the type. This temporary object is deduced to type int by default, and lives while the lambda is alive.

By setting mutable you can both modify counter and this

Aside: since it appears that you're inserting into a map/unordered map, you're probably better off with the following:

#include <algorithm> // For transform
#include <iterator>  // For inserter

Constructor(const vector<string>& names) {
    auto const example = [counter = 1](const string& item) mutable {
        return {item, counter++};
    };
    std::transform(names.begin(), names.end(),
                   std::inserter(nameMapping, nameMapping.end()), example);
}

By moving the nameMapping call outside of the lambda, you don't have to confuse yourself with what is in scope and what is not.

Also, you can avoid unnecessary captures, and anything else that might confuse yourself or other readers in the future.

Upvotes: 5

Related Questions