При использовании bind() аргументы передаются по ссылке, даже если адаптер ref() не используется

У меня есть следующий код С++ 11:

#include <iostream>
#include <functional>

using namespace std;
using namespace placeholders;

void f(int a, int b)
{
    cout << a << " " << b << endl;
}

void g(int& a, int& b)
{
    a *= 2;
    b *= 3;
}

int main()
{
    int a = 100;
    int b = 200;
    auto greversed = bind(g,_2,_1);
    greversed(b,a);
    f(a,b);
    greversed(ref(b),ref(a));
    f(a,b);
}

Основываясь на моем чтении "Язык программирования С++ 4th Edition" (Stroustrup) с. 968 Я ожидал бы, что во время первого вызова greversed (b, a) копия a и b будет передана ссылкой на g() и что только второй вызов фактически передаст a и b в g() по ссылке.

Пример кода, приведенного на стр. 968:

void incr(int& i)
{
    ++i;
}

void user()
{
    int i =1;
    incr(i);                     // i becomes 2
    auto inc = bind(incr,_1);
    inc(i);                     // i stays 2; inc(i) incremented a local copy of i
}

Запустив этот код, я увеличил его дважды, несмотря на то, что говорят комментарии.

Для моей программы ожидаемый результат:

100 200
200 600

Однако, когда я компилирую этот код под Ubuntu, используя "g++ -std = С++ 11 test.cpp", я получаю следующий вывод:

200 600
400 1800

Похоже, что a и b передаются по ссылке независимо от того, используется ли адаптер ref() или нет.

Ответ 1

Введение

std::placeholders::_* работает путем безупречной пересылки типов, которые впоследствии занимают их место.

Это означает, что, поскольку вы передаете a и b (которые являются lvalues), чтобы сгладить эти lvalues, они будут перенаправлены на g, точно так же, как и они.


Это объясняется в разделе [func.bind.bind]p10 стандарта (n3337), но более легкое для понимания объяснение можно найти здесь:


Источник путаницы

Я не читал книгу, о которой вы говорите, но ваша путаница, вероятно, заключается в том, что std::bind не свяжет ссылку на аргумент, переданный в аргументе, когда вы используете не-заполнитель, вместо этого он будет скопируйте аргумент.

Этот пример, надеюсь, поможет понять разницу между использованием std:: placeholder и передачей значения, которое должно быть привязано.

int main () {
  auto f = [](int& r1, int& r2) {
    r1 *= 2;
    r2 *= 2;
  };

  int  a = 1;
  int  b = 2;

  auto x = std::bind (f, a, std::placeholders::_1); // a copy of `a` will be stored
                                                    // inside `x`

  x (b);  // pass the copy of `a`, and perfectly-forward `b`, to `f`

  std::cout << "a: " << a << std::endl; 
  std::cout << "b: " << b << std::endl;
}
a: 1
b: 4