将 class 的方法作为 std::function 参数传递时出现问题

Problem passing a method of a class as a std::function parameter

我正在尝试编写这个函数:

void CChristianLifeMinistryEditorDlg::PerformAutoAssignForAssignment(
    const MSAToolsLibrary::AssignmentType eAssignType, 
    const CString strStartingName, 
    std::function<void(CString)> funcSetAssignName)
{
    CString strName = L"abc";
    CChristianLifeMinistryEntry *pEntry = xxx;
    // ...
    pEntry->funcSetAssignName(strName);
}

我很难将函数传递给它:

PerformAutoAssignForAssignment(MSAToolsLibrary::AssignmentType_Host, L"Name 1" );
PerformAutoAssignForAssignment(MSAToolsLibrary::AssignmentType_CoHost, L"Name 2");
PerformAutoAssignForAssignment(MSAToolsLibrary::AssignmentType_ConductorCBS, L"Name 3");
PerformAutoAssignForAssignment(MSAToolsLibrary::AssignmentType_ReaderCBS, L"Name 4");

目前我没有传递函数:

void CChristianLifeMinistryEditorDlg::PerformAutoAssignForAssignment(
    const MSAToolsLibrary::AssignmentType eAssignType, 
    const CString strStartingName)
{
    CString strName = L"abc";
    CChristianLifeMinistryEntry *pEntry = xxx;
    // ...
    pEntry->funcSetAssignName(strName);

    if (eAssignType == MSAToolsLibrary::AssignmentType_ConductorCBS)
    {
        pEntry->SetCBSConductor(strNameToUse);
    }
    else if (eAssignType == MSAToolsLibrary::AssignmentType_ReaderCBS)
    {
        pEntry->SetCBSReader(strNameToUse);
    }
    else if (eAssignType == MSAToolsLibrary::AssignmentType_Host)
    {
        pEntry->SetVideoHost(strNameToUse);
    }
    else if (eAssignType == MSAToolsLibrary::AssignmentType_CoHost)
    {
        pEntry->SetVideoCohost(strNameToUse);
    }

}

你能看出我在做什么吗?我曾希望避免使用 if/else 阶梯,因为最终我将有更多功能添加到阶梯以用于其他作业。我曾希望我可以将它们作为函数传递。

为了使 class 的方法可调用,您必须提供 this 对象。 可以通过两种方式将这种方法包装在 std::function 中。它们都将特定的 class 实例与方法相关联,使其可调用:

  1. 使用 std::bind - 请参阅文档:std::bind, and a specific Whosebug post regarding this issue: Using generic std::function objects with member functions in one class.

  2. 使用 lambda 函数,将 this 上下文作为 lambda 对象的数据成员。在此处查看一些一般信息:What is a lambda expression in C++11?.

下面的代码演示了这两种方式。 我用的是std::string而不是MFC的CString,但是原理是一样的

#include <iostream>
#include <string>
#include <functional>

struct A
{
    void Do1(std::string const & str) { std::cout << "A::Do1" << std::endl; };
    void Do2(std::string const & str) { std::cout << "A::Do2" << std::endl; };
    void Do3(std::string const & str) { std::cout << "A::Do3" << std::endl; };
};


struct Dlg
{
    A a;

    void Perform(std::string const & str, std::function<void(std::string)> funcSetAssignName)
    {
        funcSetAssignName(str);
    }

    void m()
    {
        std::string str = "abc";

        // Pass method as std::function using std::bind:
        Perform(str, std::bind(&A::Do1, a, std::placeholders::_1));
        Perform(str, std::bind(&A::Do2, a, std::placeholders::_1));
        Perform(str, std::bind(&A::Do3, a, std::placeholders::_1));

        // Pass method as std::function using lambdas:
        Perform(str, [this](std::string const & str) { this->a.Do1(str); });
        Perform(str, [this](std::string const & str) { this->a.Do2(str); });
        Perform(str, [this](std::string const & str) { this->a.Do3(str); });
    }
};

int main()
{
    Dlg dlg;
    dlg.m();
    return 0;
}

输出:

A::Do1
A::Do2
A::Do3
A::Do1
A::Do2
A::Do3

更新:
在下面的评论中关注 OP 的问题:
为了让您确定在Perform中应用方法的实例,您可以使用指向方法的指针。缺点是您传递的方法必须是指定的 class(在我下面的示例中为 A)。 IE。你失去了从各种classes(或全局)传递可调用文件的能力。

参见下面的代码示例:

#include <iostream>
#include <string>

struct A
{
    void Do1(std::string const & str) { std::cout << "A::Do1" << std::endl; };
    void Do2(std::string const & str) { std::cout << "A::Do2" << std::endl; };
    void Do3(std::string const & str) { std::cout << "A::Do3" << std::endl; };
};


struct Dlg
{
    A a;

    void Perform(std::string const & str, void (A::*funcSetAssignName)(std::string const & str))
    {
        // Here the instance for class A can be determined (I simply used `a`):
        (a.*funcSetAssignName)(str);
    }

    void m()
    {
        std::string str = "abc";

        // Pass pointer to method:
        Perform(str, &A::Do1);
        Perform(str, &A::Do2);
        Perform(str, &A::Do3);
    }
};

int main()
{
    Dlg dlg;
    dlg.m();
    return 0;
}

您可以在此处查看更多示例:Calling C++ member functions via a function pointer