如何使用 lambdas 将 std::function 与成员函数一起使用?

How to use lambdas to use std::function with member functions?

我正在尝试将一些全局函数移动到 class 中。当前代码如下所示:

struct S{};

void f(S* s, int x, double d){}
void g(S* s, int x, double d){}
/*
...
...*/
void z(S* s, int x, double d){}

int main()
{
    function<void(S*, int, double)> fp;
    
    //switch(something)
    //case 1:
        fp = f;
    //case 2:
        fp = g;
    //case n:
        fp = z;
}

假设我想将上面的代码更新为类似(下面的代码无法编译):

#include <iostream>
#include <functional>

using namespace std;

struct S
{
    void f(int x, double d){}
    void g(int x, double d){}
    /*
    ...
    ...*/
    void z(int x, double d){}    

    void method_that_has_some_logic_to_use_the_others_above(int x, double d)
    {
        function<void(int, double)> fp; // How do I have to declare here?
        //switch(something)
        //case 1:
        fp = f; // How can achieve something like this using lambdas here?
    //case 2:
        fp = g;
    //case n:
        fp = z;

        fp(x, d);
    }

};


int main()
{
    S s;
    s.method_that_has_some_logic_to_use_the_others_above(10, 5.0);
}

我见过一些使用 std::bind 的解决方案,但在某处阅读以避免使用它并更喜欢 lambda。我使用的是 C++17,但我对 lambda 没有什么经验,无法用我在其他答案中找到的示例弄清楚如何解决它。

成员函数需要一个特定的class对象来调用,所以你需要做

function<void(S*, int, double)> fp = &S::f; 
fp(this, x, d);

或者使用lambda捕获特定的class对象并在内部调用其成员函数

function<void(int, double)> fp = [this](int x, double d) { this->f(x, d); };
fp(x, d);

Demo

注意: 我提出了另一种方法,因为 OP 无法证明使用 std::function 的合理性(并且因为对字面问题已经被康桐蔚给过了)。


只要您只是在要调用的具有相同签名 的成员函数之间进行选择,std::function 就会产生不必要的开销。开销可能并不重要,如果您需要扩展到简单的成员函数调用之外,您确实有更多的灵活性。另一方面,pointer-to-member-function 提供类似的功能而没有开销。

缺点(对我来说)是语法。本例中的类型是 void (S::*)(int, double),如果您不习惯 pointers-to-member,则很难阅读。最重要的是,声明的变量位于类型的中间,这再次损害了可读性。不过,如果您可以接受语法:

// Placeholder for the decision-making process:
char choose_fun() { return 'g'; }

// Same setup for `S` as in the question, except with the following added:
void S::other_method(int x, double d)
{
    void (S::* fp)(int, double);  // Manual type specification
    // decltype(&S::other_method) fp;  // Possibly easier to read

    switch (choose_fun()) {
        case 'f': fp = &S::f; break;
        case 'g': fp = &S::g; break;
        // ...
    }

    // More processing?

    // The syntax for invoking a pointer-to-member is also a bit weird.
    (this->*fp)(x, d);
}

实际上,如果设置真的像问题中提出的那样简单(一个简单的 switch 来决定调用什么),我可能不会在指针或 std::function 中费心这个设置。我会存储 switch 使用的值,然后通过辅助函数调用这些函数。 (如果没有额外处理,那么other_method就简化为helper函数。)

// The helper function.
void S::choose_method(char choice, int x, double d)
{
    switch (choice) {
        // Returning void is legal and takes the place of having a `break`.
        case 'f': return f(x, d);
        case 'g': return g(x, d);
        // ...
    }
}

// Now the other method needs very little code in addition to the additional
// processing, whatever that is. This also brings the code closer to the
// ideal of one task per function.
void S::other_method(int x, double d)
{
    char choice = choose_fun();

    // More processing
        
    choose_method(choice, x, d);
}