简体   繁体   English

std :: bind如何与成员函数一起使用

[英]How std::bind works with member functions

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? 我认为这是因为std::bind需要一个上下文,对吗? Is always like this? 总是这样吗? Has std::bind some sort of implementation to require a reference when the first argument is a member function? 当第一个参数是成员函数时, std::bind某种实现需要引用吗?

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. std::bind()的第一个参数是一个标识如何调用函数的对象。 In the above case it is a pointer to function with type double(*)(double, double) . 在上面的例子中,它是一个指向函数的指针,类型为double(*)(double, double) Any other callable object with a suitable function call operator would do, too. 任何其他具有合适的函数调用操作符的可调用对象也可以。

Since member functions are quite common, std::bind() provides support for dealing with pointer to member functions. 由于成员函数非常常见,因此std::bind()支持处理指向成员函数的指针。 When you use &print_sum you just get a pointer to a member function, ie, an entity of type void (Foo::*)(int, int) . 当你使用&print_sum你只需要一个指向成员函数的指针,即void (Foo::*)(int, int)类型的实体。 While function names implicitly decay to pointers to functions, ie, 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 & . 虽然函数名隐式地衰减到指向函数的指针,即&可以省略,但对于成员函数(或数据成员,对于那个问题)则不一样:要获得指向成员函数的指针,必须使用&

Note that a pointer to member is specific to a class but it can be used with any object that class. 请注意,指向成员的指针特定于某个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#). 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, eg, by use std::mem_fn() with its first argument. 在内部, std::bind()检测到传递给成员函数的指针,并且很可能将其转换为可调用对象,例如,通过使用std::mem_fn()及其第一个参数。 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. 由于非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. 当使用指针构件与std::bind()的第二个参数std::bind()相应需要指定当对象是来自。 In your example 在你的例子中

std::bind(&Foo::print_sum, &foo, 95, _1)

the resulting callable object uses &foo , ie, a pointer to foo (of type Foo* ) as the object. 生成的可调用对象使用&foo ,即指向foo (类型为Foo* )的指针作为对象。 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. std::bind()非常聪明,可以使用看起来像指针的任何东西,任何可转换为适当类型的引用的东西(如std::reference_wrapper<Foo> ),或者对象的[copy]作为对象时第一个参数是指向成员的指针。

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. 函数apply()只获取两个指向Foo对象的指针和一个指向成员函数的指针。 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. 在内部, 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. apply()两个指针和&Foo::f调用apply() ,它的行为与在相应对象上调用成员f()行为完全相同。 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). 同样,当使用两个指针和&Foo::g调用apply() ,它的行为与在相应对象上调用成员g()行为完全相同(语义行为相同,但编译器可能有很多内联函数的时间越来越难,并且在涉及成员的指针时通常会失败。

From std::bind docs : 来自std :: bind docs

bind( F&& f, Args&&... args ); where f is a Callable , in your case that is a pointer to member function. 其中f是Callable ,在您的情况下是指向成员函数的指针。 This kind of pointers has some special syntax compared to pointers to usual functions: 与通常函数的指针相比,这种指针有一些特殊的语法:

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. std::bind (以及一般的 std::invoke )以统一的方式涵盖所有这些情况。 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 pointer to Foo , like in example you had. 如果fFoo的成员指针,则提供给bind的第一个Arg应该是Foo的实例( bind(&Foo::print_sum, foo, ...)也可以,但foo被复制)或者一个指向 Foo指针 ,就像你的例子一样。

Here is some more reading about pointers to members , and 1 and 2 gives full information about what bind expects and how it invokes stored function. 下面是关于成员指针的更多内容, 12提供了有关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); }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM