C++ std::bind 如何与成员函数一起工作
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37636373/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How std::bind works with member functions
提问by Tarod
I'm working with std::bind
but I still don't get how it works when we use it with member class functions.
我正在std::bind
使用它,但是当我们将它与成员类函数一起使用时,我仍然不明白它是如何工作的。
If we have the following function:
如果我们有以下函数:
double my_divide (double x, double y) {return x/y;}
I understand perfectly well the next lines of code:
我完全理解接下来的代码行:
auto fn_half = std::bind (my_divide,_1,2); // returns x/2
std::cout << fn_half(10) << '\n'; // 5
But now, with the following code where we have a bind to member function I have some questions.
但是现在,使用以下代码绑定到成员函数时,我有一些问题。
struct Foo {
void print_sum(int n1, int n2)
{
std::cout << n1+n2 << '\n';
}
int data = 10;
};
Foo foo;
auto f = std::bind(&Foo::print_sum, &foo, 95, _1);
f(5);
Why is the first argument a reference? I'd like to get a theoretical explanation.
The second argument is a reference to the object and it's for me the most complicated part to understand. I think it's because
std::bind
needs a context, am I right? Is always like this? Hasstd::bind
some sort of implementation to require a reference when the first argument is a member function?
为什么第一个参数是引用?我想得到一个理论上的解释。
第二个参数是对对象的引用,对我来说这是最难理解的部分。我认为这是因为
std::bind
需要上下文,对吗?总是这样吗?std::bind
当第一个参数是成员函数时,是否有某种实现需要引用?
回答by Dietmar Kühl
When you say "the first argument is a reference" you surely meant to say "the first argument is a pointer": the &
operator takes the address of an object, yielding a pointer.
当您说“第一个参数是一个引用”时,您肯定是在说“第一个参数是一个指针”:&
运算符获取对象的地址,产生一个指针。
Before answering this question, let's briefly step back and look at your first use of std::bind()
when you use
在回答这个问题之前,让我们先简单地退后一步,看看你第一次使用的std::bind()
时候
std::bind(my_divide, 2, 2)
you provide a function. When a function is passed anywhere it decays into a pointer. The above expression is equivalent to this one, explicitly taking the address
你提供一个函数。当一个函数被传递到任何地方时,它会衰减为一个指针。上面的表达式等价于这个,显式取地址
std::bind(&my_divide, 2, 2)
The first argument to std::bind()
is an object identifying how to call a function. In the above case it is a pointer to function with type double(*)(double, double)
. Any other callable object with a suitable function call operator would do, too.
的第一个参数std::bind()
是一个标识如何调用函数的对象。在上面的例子中,它是一个指向函数类型的指针double(*)(double, double)
。任何其他具有合适的函数调用运算符的可调用对象也可以。
Since member functions are quite common, std::bind()
provides support for dealing with pointer to member functions. When you use &print_sum
you just get a pointer to a member function, i.e., an entity of type void (Foo::*)(int, int)
. While function names implicitly decay to pointers to functions, i.e., the &
can be omitted, the same is not true for member functions (or data members, for that matter): to get a pointer to a member function it is necessary to use the &
.
由于成员函数很常见,因此std::bind()
提供了处理指向成员函数的指针的支持。当您使用时,&print_sum
您只会获得一个指向成员函数的指针,即类型为 的实体void (Foo::*)(int, int)
。虽然函数名称隐式衰减为指向函数的指针,即&
可以省略,但对于成员函数(或数据成员,就此而言)并非如此:要获得指向成员函数的指针,必须使用&
.
Note that a pointer to member is specific to a class
but it can be used with any object that class. That is, it is independent of any particular object. C++ doesn't have a direct way to get a member function directly bound to an object (I think in C# you can obtain functions directly bound to an object by using an object with an applied member name; however, it is 10+ years since I last programmed a bit of C#).
请注意,指向成员的指针特定于 a,class
但它可以与该类的任何对象一起使用。也就是说,它独立于任何特定对象。C++ 没有直接的方法来获取直接绑定到对象的成员函数(我认为在 C# 中,您可以通过使用具有应用成员名称的对象来获取直接绑定到对象的函数;但是,距今已有 10 多年了我上次编写了一些 C#)。
Internally, std::bind()
detects that a pointer to a member function is passed and most likely turns it into a callable objects, e.g., by use std::mem_fn()
with its first argument. Since a non-static
member function needs an object, the first argument to the resolution callable object is either a reference or a [smart] pointer to an object of the appropriate class.
在内部,std::bind()
检测到成员函数的指针被传递,并且很可能将其转换为可调用对象,例如,通过使用std::mem_fn()
其第一个参数。由于非static
成员函数需要一个对象,解析可调用对象的第一个参数是一个引用或一个指向适当类的对象的 [智能] 指针。
To use a pointer to member function an object is needed. When using a pointer to member with std::bind()
the second argument to std::bind()
correspondingly needs to specify when the object is coming from. In your example
要使用指向成员函数的指针,需要一个对象。当使用带有std::bind()
第二个参数的成员指针时,std::bind()
相应地需要指定对象来自何时。在你的例子中
std::bind(&Foo::print_sum, &foo, 95, _1)
the resulting callable object uses &foo
, i.e., a pointer to foo
(of type Foo*
) as the object. std::bind()
is smart enough to use anything which looks like a pointer, anything convertible to a reference of the appropriate type (like std::reference_wrapper<Foo>
), or a [copy] of an object as the object when the first argument is a pointer to member.
生成的可调用对象使用&foo
,即指向foo
(类型Foo*
)的指针作为对象。当第一个参数是指向成员std::bind()
的指针时,它足够聪明,可以使用任何看起来像指针的东西,任何可转换为适当类型的引用(如std::reference_wrapper<Foo>
)或对象的 [副本] 作为对象的对象。
I suspect, you have never seen a pointer to member - otherwise it would be quite clear. Here is a simple example:
我怀疑,您从未见过指向成员的指针 - 否则就很清楚了。这是一个简单的例子:
#include <iostream>
struct Foo {
int value;
void f() { std::cout << "f(" << this->value << ")\n"; }
void g() { std::cout << "g(" << this->value << ")\n"; }
};
void apply(Foo* foo1, Foo* foo2, void (Foo::*fun)()) {
(foo1->*fun)(); // call fun on the object foo1
(foo2->*fun)(); // call fun on the object foo2
}
int main() {
Foo foo1{1};
Foo foo2{2};
apply(&foo1, &foo2, &Foo::f);
apply(&foo1, &foo2, &Foo::g);
}
The function apply()
simply gets two pointers to Foo
objects and a pointer to a member function. It calls the member function pointed to with each of the objects. This funny ->*
operator is applying a pointer to a member to a pointer to an object. There is also a .*
operator which applies a pointer to a member to an object (or, as they behave just like objects, a reference to an object). Since a pointer to a member function needs an object, it is necessary to use this operator which asks for an object. Internally, std::bind()
arranges the same to happen.
该函数apply()
仅获取两个指向Foo
对象的指针和一个指向成员函数的指针。它调用每个对象指向的成员函数。这个有趣的->*
操作符将指向成员的指针应用于指向对象的指针。还有一个.*
运算符将指向成员的指针应用于对象(或者,因为它们的行为就像对象,所以是对对象的引用)。由于指向成员函数的指针需要一个对象,因此有必要使用这个请求对象的操作符。在内部,std::bind()
安排同样的事情发生。
When apply()
is called with the two pointers and &Foo::f
it behaves exactly the same as if the member f()
would be called on the respective objects. Likewise when calling apply()
with the two pointers and &Foo::g
it behaves exactly the same as if the member g()
would be called on the respective objects (the semantic behavior is the same but the compiler is likely to have a much harder time inlining functions and typically fails doing so when pointers to members are involved).
当apply()
使用两个指针调用时,&Foo::f
它的行为与f()
在相应对象上调用成员的行为完全相同。同样,当apply()
使用两个指针调用时,&Foo::g
它的行为与g()
在相应对象上调用成员的行为完全相同(语义行为是相同的,但编译器可能更难内联函数,并且通常在以下情况下失败)涉及到成员的指针)。
回答by Dmitry Panteleev
From std::bind docs:
来自std::bind 文档:
bind( F&& f, Args&&... args );
where f is a Callable
, in your case that is a pointer to member function. This kind of pointers has some special syntax compared to pointers to usual functions:
bind( F&& f, Args&&... args );
其中 f 是 a Callable
,在您的情况下是指向成员函数的指针。与指向普通函数的指针相比,这种指针有一些特殊的语法:
typedef void (Foo::*FooMemberPtr)(int, int);
// obtain the pointer to a member function
FooMemberPtr a = &Foo::print_sum; //instead of just a = my_divide
// use it
(foo.*a)(1, 2) //instead of a(1, 2)
std::bind
(and std::invoke
in general) covers all these cases in a uniform way. If f
is a pointer-to-member of Foo
, then the first Arg
provided to bind is expected to be an instance of Foo
(bind(&Foo::print_sum, foo, ...)
also works, but foo
is copied) or a pointerto Foo
, like in example you had.
std::bind
(std::invoke
通常)以统一的方式涵盖所有这些情况。如果f
是的指针到构件Foo
,则第一Arg
提供给绑定预计的一个实例Foo
(bind(&Foo::print_sum, foo, ...)
也可以,但是foo
被复制)或指针到Foo
,像例如在你有。
Here is some more reading about pointers to members, and 1and 2gives full information about what bind expects and how it invokes stored function.
这里有更多关于成员指针的阅读,1和2提供了关于 bind 期望什么以及它如何调用存储函数的完整信息。
You also can use lambdas instead std::bind
, which could be more clear:
您也可以使用 lambdas 代替std::bind
,这可能更清楚:
auto f = [&](int n) { return foo.print_sum(95, n); }