如何在 C++ 变量中存储已知签名的特定对象的成员函数?

How to store a specific object's member function of known signature in a variable in C++?

在某些对象实例上存储对特定签名的非静态成员函数的引用的推荐方法是什么?在某种程度上,调用代码不需要知道对象的 class(即没有强制转换),只需知道函数是正确的签名即可。

例如,如果有两个不同的 class 具有相同 void(int) 签名的功能,例如

class Foo {
public:
  int myInt;
  
  void multMyInt(int multiplier) {
    myInt *= multiplier;
  }
};

class Bar {
public:
  bool isBig;
  
  void setMySize(int size) {
    isBig = size > 100;
  }
};

这段代码应该是什么样的?

int main() {
  Foo* myFoo = new Foo();
  Bar* myBar = new Bar();

  int x = 10;

  callIntFunc(x, /* myFoo->multMyInt() */);
  callIntFunc(x, /* myBar->setMySize() */);
}

// Calls any function which matches void(int).
void callIntFunc(int inInt, /* function stored in some way */) {
  // call given function with inInt as the parameter
}

仅使用标准库执行此操作的推荐方法是什么?

p.s。如果这是一个重复的问题,我们深表歉意。我搜索了想到的所有术语,但找不到。

由于您不仅要存储指向成员函数的指针,还要存储函数应该访问的对象,因此您需要一种方法将两者存储在同一种对象中。

由于 C++ 具有 lambda 函数,您可以简单地使用它们,就像在您的代码修改后的以下示例中一样。

class Foo {
    public:
        int myInt;

        void multMyInt(int multiplier) {
            std::cout << "Calls multMyInt" << std::endl;
            myInt *= multiplier;
        }
};

class Bar {
    public:
        bool isBig;

        void setMySize(int size) {
            std::cout << "Calls setMySize" << std::endl;
            isBig = size > 100; 
        }
};

// Calls any function which matches void(int).
void callIntFunc(int inInt, auto& func )
{
    func(inInt);
}


int main() {
    Foo* myFoo = new Foo();
    Bar* myBar = new Bar();

    auto multMyInt = [myFoo](int parm){ myFoo->multMyInt(parm);};
    auto setMySize = [myBar](int parm){ myBar->setMySize(parm);};

    int x = 10;



    callIntFunc(x, multMyInt);
    callIntFunc(x, setMySize);
}

在 C++11 之前,您必须使用 std::bind 但实际上不再需要了。

除了将 auto 与 lambda 结合使用外,您还可以使用 std::function,这会导致:

// Calls any function which matches void(int).
void callIntFunc(int inInt, std::function<void(int)>& func )
{
    func(inInt);
}


int main() {
    Foo* myFoo = new Foo();
    Bar* myBar = new Bar();

    std::function<void(int)> multMyInt = [myFoo](int parm){ myFoo->multMyInt(parm);};
    std::function<void(int)> setMySize = [myBar](int parm){ myBar->setMySize(parm);};

    int x = 10;



    callIntFunc(x, multMyInt);
    callIntFunc(x, setMySize);
}

使用 auto 的模板化版本需要更多的程序内存,因为它为每个函数类型生成一个新实例,但速度很快。

std::function 版本需要较少的程序 space 但会产生一点运行时开销。这取决于您的需求,您喜欢什么!

评论更新:

在使用 lambda 的情况下,与在使用指针或引用的所有其他情况下一样,范围没有什么特别之处。但是你要小心:

  • 如果通过引用捕获,调用函数/lambda 时引用的对象必须是存活的。
  • 如果您捕获对象的副本,您将获得一个新的独立副本。因此,只要 lambda 还存在,它就有效,但您引用的是副本而不是原始对象。

auto的类型是什么:

每个 lambda 都会产生一个新类型。类型本身总是被实现隐藏和生成。这就是为什么你必须使用 auto.

使用std::bind_front。它与另一个答案中建议的自定义 lambda 做同样的事情,但语法不那么冗长。

auto func = std::bind_front(&Foo::multMyInt, myFoo);
func(42);

请注意,我们将 class 指针 作为第二个参数传递。如果我们改为按值传递,class 实例将被复制到函子中。

此处auto扩展为一些未指定的类型,但您可以将其替换为std::function<void(int)>