我如何传递一个成员函数,其中一个自由函数是预期的?

问题如下: 考虑这段代码:

#include <iostream>




class aClass
{
public:
void aTest(int a, int b)
{
printf("%d + %d = %d", a, b, a + b);
}
};


void function1(void (*function)(int, int))
{
function(1, 1);
}


void test(int a,int b)
{
printf("%d - %d = %d", a , b , a - b);
}


int main()
{
aClass a;


function1(&test);
function1(&aClass::aTest); // <-- How should I point to a's aClass::test function?
}

如何使用 aaClass::test作为 function1的参数?我想访问类的一个成员。

309580 次浏览

指向成员函数的指针与指向函数的指针不同。为了通过指针使用成员函数,您需要一个指向它的指针(显然)和一个应用它的对象。因此,function1的适当版本应该是

void function1(void (aClass::*function)(int, int), aClass& a) {
(a.*function)(1, 1);
}

并称之为:

aClass a; // note: no parentheses; with parentheses it's a function declaration
function1(&aClass::test, a);

使用函数指针没有任何错误。但是,指向非静态成员函数的指针不同于普通的函数指针: 成员函数需要在作为隐式参数传递给函数的对象上调用。上面的成员函数的签名如下

void (aClass::*)(int, int)

而不是你试图使用的类型

void (*)(int, int)

一种方法可以包括创建成员函数 static,在这种情况下,它不需要调用任何对象,您可以将它与类型 void (*)(int, int)一起使用。

如果你需要访问你的类 还有的任何非静态成员,你需要坚持使用函数指针,例如,因为函数是 C 接口的一部分,你最好的选择是总是传递一个 void*给你的函数,接受函数指针,通过一个转发函数调用你的成员,这个转发函数从 void*获得一个对象,然后调用成员函数。

在适当的 C + + 接口中,您可能希望看到函数采用模板参数,以便函数对象使用任意类类型。如果不希望使用模板化的接口,那么应该使用类似于 std::function<void(int, int)>的东西: 您可以为它们创建一个适当的可调用函数对象,例如,使用 std::bind()

使用类类型的模板参数或适当的 std::function<...>的类型安全方法优于使用 void*接口,因为它们消除了由于转换为错误类型而导致的错误的可能性。

为了说明如何使用函数指针调用成员函数,这里有一个例子:

// the function using the function pointers:
void somefunction(void (*fptr)(void*, int, int), void* context) {
fptr(context, 17, 42);
}


void non_member(void*, int i0, int i1) {
std::cout << "I don't need any context! i0=" << i0 << " i1=" << i1 << "\n";
}


struct foo {
void member(int i0, int i1) {
std::cout << "member function: this=" << this << " i0=" << i0 << " i1=" << i1 << "\n";
}
};


void forwarder(void* context, int i0, int i1) {
static_cast<foo*>(context)->member(i0, i1);
}


int main() {
somefunction(&non_member, nullptr);
foo object;
somefunction(&forwarder, &object);
}

@ Pete Becker 的回答很好,但是你也可以不用把 class实例作为显式参数传递给 C + + 11中的 function1:

#include <functional>
using namespace std::placeholders;


void function1(std::function<void(int, int)> fun)
{
fun(1, 1);
}


int main (int argc, const char * argv[])
{
...


aClass a;
auto fp = std::bind(&aClass::test, a, _1, _2);
function1(fp);


return 0;
}

你们现在可以停止撞头了。下面是成员函数的包装器,它支持将普通 C函数作为参数的 存在函数。thread_local指令是这里的关键。

Http://cpp.sh/9jhk3

// Example program
#include <iostream>
#include <string>


using namespace std;


typedef int FooCooker_ (int);


// Existing function
extern "C" void cook_10_foo (FooCooker_ FooCooker) {
cout << "Cooking 10 Foo ..." << endl;
cout << "FooCooker:" << endl;
FooCooker (10);
}


struct Bar_ {
Bar_ (int Foo = 0) : Foo (Foo) {};
int cook (int Foo) {
cout << "This Bar got " << this->Foo << endl;
if (this->Foo >= Foo) {
this->Foo -= Foo;
cout << Foo << " cooked" << endl;
return Foo;
} else {
cout << "Can't cook " <<  Foo << endl;
return 0;
}
}
int Foo = 0;
};


// Each Bar_ object and a member function need to define
// their own wrapper with a global thread_local object ptr
// to be called as a plain C function.
thread_local static Bar_* Bar1Ptr = NULL;
static int cook_in_Bar1 (int Foo) {
return Bar1Ptr->cook (Foo);
}


thread_local static Bar_* Bar2Ptr = NULL;
static int cook_in_Bar2 (int Foo) {
return Bar2Ptr->cook (Foo);
}


int main () {
Bar1Ptr = new Bar_ (20);
cook_10_foo (cook_in_Bar1);
  

Bar2Ptr = new Bar_ (40);
cook_10_foo (cook_in_Bar2);
  

delete Bar1Ptr;
delete Bar2Ptr;
return 0;
}

请对此方法的任何问题进行评论。

其他答案无法调用 存在普通的 C函数: http://cpp.sh/8exun

从2011年开始,如果你可以改变 function1,这样做:

#include <functional>
#include <cstdio>


using namespace std;


class aClass
{
public:
void aTest(int a, int b)
{
printf("%d + %d = %d", a, b, a + b);
}
};


template <typename Callable>
void function1(Callable f)
{
f(1, 1);
}


void test(int a,int b)
{
printf("%d - %d = %d", a , b , a - b);
}


int main()
{
aClass obj;


// Free function
function1(&test);


// Bound member function
using namespace std::placeholders;
function1(std::bind(&aClass::aTest, obj, _1, _2));


// Lambda
function1([&](int a, int b) {
obj.aTest(a, b);
});
}

(现场演示)

还要注意,我修复了破损的对象定义(aClass a();声明了一个函数)。

我问了一个类似的问题(从其他类传递 void 的 C + + open 框架) ,但是我找到的答案更加清晰,所以下面是对未来记录的解释:

使用 std: : 函数更容易:

 void draw(int grid, std::function<void()> element)

然后打电话给:

 grid.draw(12, std::bind(&BarrettaClass::draw, a, std::placeholders::_1));

或者更简单:

  grid.draw(12, [&]{a.draw()});

创建一个 lambda 调用通过引用捕获它的对象

我使成员功能为静态和所有工作:

#include <iostream>


class aClass
{
public:
static void aTest(int a, int b)
{
printf("%d + %d = %d\n", a, b, a + b);
}
};


void function1(int a,int b,void function(int, int))
{
function(a, b);
}


void test(int a,int b)
{
printf("%d - %d = %d\n", a , b , a - b);
}


int main (int argc, const char* argv[])
{
aClass a;


function1(10,12,test);
function1(10,12,a.aTest); // <-- How should I point to a's aClass::test function?


getchar();return 0;
}

不知道为什么这个难以置信的简单解决方案被拒绝了:

#include <stdio.h>


class aClass
{
public:
void aTest(int a, int b)
{
printf("%d + %d = %d\n", a, b, a + b);
}
};


template<class C>
void function1(void (C::*function)(int, int), C& c)
{
(c.*function)(1, 1);
}
void function1(void (*function)(int, int)) {
function(1, 1);
}


void test(int a,int b)
{
printf("%d - %d = %d\n", a , b , a - b);
}


int main (int argc, const char* argv[])
{
aClass a;


function1(&test);
function1<aClass>(&aClass::aTest, a);
return 0;
}

产出:

1 - 1 = 0
1 + 1 = 2

如果实际上不需要使用实例 a (也就是说,你可以让它像@mathengineering 的 回答一样是静态的) 你可以简单地传入一个未捕获的 lambda (它会衰变成函数指针)


#include <iostream>


class aClass
{
public:
void aTest(int a, int b)
{
printf("%d + %d = %d", a, b, a + b);
}
};


void function1(void (*function)(int, int))
{
function(1, 1);
}


int main()
{
//note: you don't need the `+`
function1(+[](int a,int b){return aClass{}.aTest(a,b);});
}

魔杖盒


注意: 如果 aClass是昂贵的建设或有副作用,这可能不是一个好办法。