如何通过引用将功能绑定到对象? [英] How to bind function to an object by reference?
问题描述
我有以下代码将成员函数绑定到该类的实例:
I have the following code to bind a member function to an instance of the class:
class Foo {
public:
int i;
void test() {
std::cout << i << std::endl;
}
};
int main() {
Foo f;
f.i = 100;
auto func = std::bind(&Foo::test, std::forward<Foo>(f));
f.i = 1000;
func();
}
但是std::bind
语句不会通过引用绑定到f
.调用func
会打印"100"而不是我想要的"1000".
But the std::bind
statement does not bind to f
by reference. Calling func
prints "100" instead of "1000" which is what I want.
但是,如果我将语句更改为采用指针,那么它将起作用.
However, If I change the statement to take a pointer it works.
auto func = std::bind(&Foo::test, &f);
但是根据我的理解,这是通过指针传递f
,并且我认为std::bind
采用r值引用Arg&&
(如
But this is passing f
by pointer by my understanding, and as I thought std::bind
takes an r-value reference Arg&&
(as shown here) how can this work?
有人可以解释一下吗?
推荐答案
使用std::forward
的注意事项
首先,std::forward
用于完美转发,即转发引用类型( l值或r值).
Note on using std::forward
First of all, std::forward
is meant to be used for perfect forwarding, i.e. to forward the reference type (l-value or r-value).
如果将 l值引用传递给std::forward
,则返回该值,同样,如果传递了 r-value 引用,则r值返回.与std::move
总是返回一个r值引用相反,它起作用.还请记住,命名 r值引用是l值引用.
If you pass an l-value reference to std::forward
that is what is returned, and likewise if an r-value reference is passed then an r-value is returned. This works as opposed to std::move
that will always return an r-value reference. Also remember that named r-value references are l-value references.
/* Function that takes r-value reference. */
void func(my_type&& t) {
// 't' is named and thus is an l-value reference here.
// Pass 't' as lvalue reference.
some_other_func(t);
// Pass 't' as rvalue reference (forwards rvalueness).
some_other_func(std::forward<my_type>(t));
// 'std::move' should be used instead as we know 't' is always an rvalue.
// e.g. some_other_func(std::move(t));
}
此外,绝对不要在以后需要从中访问某些状态的对象上使用std::forward
或std::move
.移出的对象处于未指定状态,但处于有效状态,这基本上意味着您无法对它们执行任何操作,除非销毁或重新为其分配状态.
Also you should never use std::forward
or std::move
on an object that you afterwards need to access some state from. Objects that are moved from are put in an unspecified but valid state, which basically means that you cant do anything with them except destroy or reassign a state to them.
函数std::bind
将始终复制或移动其参数.我从标准中找不到合适的引用,但 en.cppreference.com说:
The function std::bind
will always copy or move its arguments. I could not find an appropriate citation from the standard but en.cppreference.com says:
要绑定的参数将被复制或移动,除非包装在std :: ref或std :: cref中,否则绝对不会通过引用传递."
这意味着,如果您将参数作为左值引用传递,则它是副本构造的;如果您将其作为右值引用传递,则它是搬动.无论哪种方式,参数都永远不会作为引用传递.
This means that if you pass the argument(s) as an l-value reference then it is copy constructed, and if you pass it as an r-value reference then it is move constructed. In either way the argument(s) will never be passed as references.
要避免这种情况,您可以例如使用 std::ref
作为可复制的参考包装,它将在内部保留参考到呼叫站点上的变量.
To circumvent this you can e.g. use std::ref
as a copyable reference wrapper that will internally keep a reference to the variable at the call site.
auto func = std::bind(&Foo::test, std::ref(f));
或者您可以按照您的建议简单地将指针传递给f
.
Or you could simply pass a pointer to f
as you suggest.
auto func = std::bind(&Foo::test, &f);
然后发生的是,std::bind
将对通过调用f
的address-of运算符返回的临时指针进行r值引用.该指针将被复制(因为指针无法移动)到std::bind
返回的绑定包装对象中.即使指针本身已被复制,它仍将指向调用站点上的对象,您将获得所需的引用语义.
What happens then is that std::bind
will take an r-value reference to the temporary pointer returned from calling the address-of operator on f
. This pointer will be copied (as pointers can't be moved) into the bound wrapper object returned from std::bind
. Even though the pointer itself is copied, it will still point to the object at the call site and you will achieve the reference semantics you wanted.
或者使用 lambda 来通过引用捕获f
并调用函数Foo::test
.这是恕我直言,因为通常情况下lambda比std::bind
表达式更具通用性和功能,因此是推荐的方法.
Alternatively use a lambda that captures f
by reference and calls the function Foo::test
. This is IMHO the recommended approach as lambdas are more versatile and powerful than std::bind
expressions in the general case.
Foo f;
f.i = 100;
auto func = [&f] { f.test(); };
f.i = 1000;
func(); // 1000
注意:有关何时使用std::forward
的详细说明,请参见
Note: for a good explanation of when to use std::forward
see this excellent video by Scott Meyers.
这篇关于如何通过引用将功能绑定到对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!