15

I found this code in Bjarne Stroustrup's book: Page screenshot

The problem with this code is that variable i does not stay at 2, it is incremented to 3. You can check it here: https://wandbox.org/permlink/p5JC1nOA4pIpsgXb

We do not have to use the std::ref() to increment this variable. Is it a mistake in the book or something has been changed since C++11?

Praetorian
  • 106,671
  • 19
  • 240
  • 328
Evelekk
  • 159
  • 7

1 Answers1

11

The example is incorrect, bind does make a copy of its arguments unless you wrap it in std::reference_wrapper as the text correctly says, but that's not what the example shows. In the example, the argument i is passed to the functor returned by bind, not to bind itself. If the example had instead been the following, the value of i would've remained 2.

auto inc = bind(incr, i);   // a copy of i is made
inc(); // i stays 2; inc(i) incremented a local copy of i

In the example shown in the book, the argument i will be forwarded to incr, which will result in an lvalue reference to the original i being passed to the function, and the original i will be incremented.

For the relevant standard quotes, from 23.14.11.3 [func.bind.bind]/10

The values of the bound arguments v1, v2, …, vN and their corresponding types V1, V2, …, VN depend on the types TDi derived from the call to bind and the cv-qualifiers cv of the call wrapper g as follows:
...
— if the value j of is_­placeholder_­v<TDi> is not zero, the argument is std​::​forward<Uj>(uj) and its type Vi is Uj&&;

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • 1
    I deleted my answer cause I think you're right, so let me move this question over here: The functor created by `bind` should have the function: `void operator() (int&& arg) const { incr(arg); }` So passing a local `int` to a function expecting an `int&&` should have the effect of passing by reference? – Jonathan Mee Aug 15 '17 at 18:45
  • 1
    @JonathanMee No, if that were the signature `inc(i)` would not compile because you would be trying to bind an lvalue to an rvalue reference. The signature is `template void operator(T&& arg) { incr(std::forward(arg)); }`. So the example forwards a reference to the original `i` to `incr`. – Praetorian Aug 15 '17 at 18:47
  • So when passing `i` into a function that accepts an `int&&`, `arg` will actually be an `int&`? That is to say, when `arg` is forwarded to `incr`, it is an `int&` that's being forwarded? – Jonathan Mee Aug 15 '17 at 21:14
  • @JonathanMee The `_N` placeholders do perfect forwarding, which means the value category of the original argument is preserved. If `incr` was defined as `void incr(int&&)` and you tried to call `inc(i)` it wouldn't compile because you would be attempting to bind an lvalue to an rvalue reference parameter. You would have to write `inc(std::move(i))` in that case. – Praetorian Aug 15 '17 at 23:18
  • OK, what am I missing? You say that the functor operator takes an `int&&` and `i` is passed to it, but if `incr` took an `int&&` we couldn't pass `i` to it. Seems like one of those has to be wrong. – Jonathan Mee Aug 16 '17 at 04:00
  • 1
    I don't see where I've said it takes an `int&&`. If you mean the `T&&` in `template void operator()(T&&)`, then that's a [forwarding reference](https://stackoverflow.com/q/14302849/241631), previously known as a universal reference. When you pass `i` (an lvalue) to it, `T` will be deduced as `int&`, reference collapsing happens and the parameter type is `int&`. – Praetorian Aug 16 '17 at 04:31
  • so, book example was unnecessary overcomplicated with placeholder which actualy serves to same purpose as std::ref in this case – Swift - Friday Pie Aug 16 '17 at 10:27
  • Did you email Mr. Stroustrup about the error? (also +1 of course) – Barry Aug 17 '17 at 01:39
  • @Barry I'd assumed the OP would do it, but I just sent him an email. Thanks for the reminder. – Praetorian Aug 17 '17 at 04:02