13

我一直在阅读std::bindN3225 小节中的描述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), wherev1应该是a(我传入的参数,换句话说,使用binds的传入参数是一个完美的转发参数,std::forward<A1>(a1))。

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

4

3 回答 3

5

它说以下应该打印 1

不,它没有这么说。

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

是的。

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

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

于 2011-02-21T14:11:54.040 回答
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因此在回调调用时它将是有效的。

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

于 2011-02-21T13:14:18.127 回答
2

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

tid是一个类型的左值,TiDstd::forward<Ti>(ti)

好吧,我确实说过,这tidstd::forward<Ti>(ti)一个左值!但这并不是它真正要说的意思。它的意思是

tid是一个类型的左值,TiD它指的是从构造的对象std::forward<Ti>(ti)

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

于 2011-02-21T11:26:12.287 回答