Enlico
Enlico

Reputation: 28470

Relation between C++11 emulation of C++14 generalized lambda captures, and the mutable specifier

At pages 227-228 of Item 32 from Effective Modern C++, Scott Meyers presents the following chunck of code as a way to workaround the lack of generalized lambda captures in C++11.

// data is in scope
auto func =
  std::bind(
      [](const std::vector<double>& x) // I've renamed it x so there's no ambiguity in my description below
      { /* usses of x */ },
      std::move(data)
  );

Then he makes the following comment, which I don't quite understand overall.

By default, the operator() member function inside the closure class generated from a lambda is const. That has the effect of rendering all data members in the closure const within the body of the lambda. The move-constructed copy of data inside the bind object is not const, however, so to prevent that copy of data from being modified inside the lambda, the lambda's parameter is declared reference-to-const. If the lambda were declared mutable, operator() in its closure class would not be declared const, and it would be appropriate to omit const in the lambda's parameter declaration:

// data is in scope
auto func =
  std::bind(
    [](std::vector<doubld>& x) mutable // ditto
    { /* uses of x */ },
    std::move(data)
  );

I tried to break it down:

(That lambda, however, is not capturing anything in the first place, so the following points seem unrelated to me...)

So, I don't get the point of the last period If the lambda were declared [...].

Upvotes: 0

Views: 50

Answers (1)

Enlico
Enlico

Reputation: 28470

Ok, writing the question helped me understand I was simply losing sight of the initial motivation of that code: emulating a generalized lambda capture.

Maybe Scott Meyers also implies that it's natural that the writer of that code expects the bind object func to behave like the desired lambda as it was coded with the same specifiers as the actual lambda stored inside func (actually the text only refers to the mutable specifier, maybe because it's the only one worth discussing).

In other words,

  • seeing a non-mutable lambda stored inside func through std::bind, the programmer likely expects that func will behave as the corresponding non-mutable C++14 lambda, hence the const of the parameter of the actual lambda stored in func;
  • if the actual lambda is mutable, then the user expects func to behave as the corresponding mutable C++14 lambda, so he does not expect it to preserve the captured value unmodified, hence the lack of const in the second chunk of code.

Even shorter:

A non-mutable lambda stored inside func should not modify its argument(s), because the corresponding desired non-mutable C++14 lambda would not modify its captured variable(s).

Upvotes: 1

Related Questions