std :: bind(x,y)到底有什么用呢?

时间:2011-02-21 11:04:45

标签: c++ c++11 stdbind

我很难在第std::bind小节中阅读N3225中20.8.10.1的说明。它说下面应该打印1,但我认为bind应该复制其参数,因此它应该打印0。如果想要引用传递的参数,则需要使用std::ref,对吧?

void f(int &a) { a = 1; }

int main() {
  int a = 0;
  std::bind(f, a)();
  std::cout << a << std::endl;
}

GCC输出0,同意我认为的工作。但是N3225说std::bind(f, a1)将返回一个调用包装器,当wrapper()调用时会调用INVOKE(f, v1),其中v1应为a(我传入的参数)换句话说,使用binds的传入参数,这是一个完美的转发参数std::forward<A1>(a1))。

INVOKE(f, a)由20.8.2到f(a)定义。因此,这定义了对返回的调用包装器的调用传递原始参数。我错过了什么?

3 个答案:

答案 0 :(得分:5)

  

它说下面应该打印1

不,它没有这么说。

  

如果想要引用传递的参数,需要使用std :: ref,对吗?

  

但是N3225说std :: bind(f,a1)应该返回一个调用包装器,当被wrapper()调用时会调用INVOKE(f,v1),其中v1应该是a(我传入的参数,in换句话说,使用binds的传入参数,这是一个完美的转发参数,std :: forward(a1​​))。

那就是你错了。您在bind-call中传入的“绑定参数”以新创建的TiD类型对象的形式存储,每个对象都来自forward<Ti>(ti) 。通过说“tid是来自std::forward<Ti>(ti)”的TiD 构造类型的左值,可以相当清楚地说明这一点。由于参考包装的特殊处理,还有一个额外的“转换层”。见20.8.10.1.2 / 10,其中解释了viVitidTiD的关系。

答案 1 :(得分:3)

它目前打印0,因为在你调用std :: bind时,它不知道你想传递引用。它不会查看函数的签名,以查看它所采用的参数类型并进行相应的调整。

要使其正常工作,请致电

void f(int &a) { a = 1; }

int main() {
  int a = 0;
  std::bind(f, std::ref(a))();
  std::cout << a << std::endl;
}

C ++ 0x建议“完美绑定”,但这有一个巨大的危险,这可能会严重破坏依赖于当前行为的现有代码。这是一个非常简单的例子。

void myCallback( const std::string& str, int i );

function< void(int) > makeCallback( const std::string & str )
{
    return bind( myCallback, str, _1 );
}

目前,您可以依赖绑定复制您使用str传入的字符串,因此它将有效的事实来自回调的调用。

如果它“聪明地”使用“完美绑定”来存储它作为参考,它将打破这样的情况。

答案 2 :(得分:2)

哇,这令人难以置信。它将v1定义为tid,并将其定义为以下(ti是第i个完美转发绑定参数,TiD是该参数的衰减类型 - 即数组成为指针等)。

  

tid是由TiD

构建的std::forward<Ti>(ti)类型的左值

好吧,我确实说过,这tidstd::forward<Ti>(ti)而且它是一个左值!但这并不是它真正意味着什么。这意味着

  

tidTiD类型的左值,它引用从std::forward<Ti>(ti)构建的对象

现在更有意义了。因为如果std::forward<Ti>(ti)实际上是一个右值呢? “由...构造的左值”意味着我们从“...”创建一个新对象并使左值引用它。