我很难在第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)
定义。因此,这定义了对返回的调用包装器的调用传递原始参数。我错过了什么?
答案 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,其中解释了vi
和Vi
与tid
和TiD
的关系。
答案 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)
类型的左值
好吧,我确实说过,这tid
是std::forward<Ti>(ti)
而且它是一个左值!但这并不是它真正意味着什么。这意味着
tid
是TiD
类型的左值,它引用从std::forward<Ti>(ti)
构建的对象
现在更有意义了。因为如果std::forward<Ti>(ti)
实际上是一个右值呢? “由...构造的左值”意味着我们从“...”创建一个新对象并使左值引用它。