使用bind()时,即使未使用ref()适配器,也会通过引用传递参数

low*_*ion 14 c++ c++11

我有以下C++ 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);
}
Run Code Online (Sandbox Code Playgroud)

基于我对"The C++ Programming Language 4th Edition"(Stroustrup)的解读.968我希望在第一次调用greversed(b,a)期间a和b的副本将通过引用传递给g()并且只有第二个调用实际上会通过引用将a和b传递给g() .

p上给出的示例代码.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
}
Run Code Online (Sandbox Code Playgroud)

运行此代码,我会增加两次,尽管评论说.

对于我的程序,我的预期输出将是:

100 200
200 600
Run Code Online (Sandbox Code Playgroud)

但是,当我使用"g ++ -std = c ++ 11 test.cpp"在Ubuntu下编译此代码时,我得到以下输出:

200 600
400 1800
Run Code Online (Sandbox Code Playgroud)

无论是否使用ref()适配器,它都会通过引用传递a和b.

Fil*_*efp 19

介绍

std::placeholders::_*通过完美转发后来取代它们的类型.

这意味着,因为你正在传递一个b(这是左值),以greversed这些左值将被转发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;
}
Run Code Online (Sandbox Code Playgroud)
a: 1
b: 4
Run Code Online (Sandbox Code Playgroud)