我有一个函数,该函数将指向函数的指针作为参数:
void MySolver::set_pt2func(double(*pt2function)(double*))
{
pt2Function = pt2function;
}
在另一个类中,某些方法想要使用以下代码初始化实例my_solver:
double (MyClass::*pt2apply)(double* parameter) = NULL;
pt2apply = &MyClass::apply_func;
my_solver->set_pt2func(pt2apply);
最后一行显示编译错误:
error: double (MyClass::*pt2apply)(double*)
argument of type (MyClass::* )(double*) is not compatible
with argument of type double(*)(double*)
怎么修?
谢谢!
编辑
我用这个技巧
http://www.parashift.com/c++-faq/memfnptr-to-const-memfn.html
我设置了typedef:
typedef double (MyClass::*Pt2apply_func)(double*) const;
在我的方法中,我尝试这样做:
Pt2apply_func pt2apply = &MyClass::apply_func;
my_solver->set_pt2func(pt2apply);
并使用
&MyClass
和再次兼容的类型显示编译错误。其他方式来处理我的问题?谢谢!
最佳答案
您需要一个指向函数的指针:
double(*)(double*)
您拥有的是指向成员函数的指针:
double (MyClass::*)(double*)
这些不是同一回事。甚至所有类似的东西。第一个可以直接调用;第二个可以直接调用。第二个只能使用
->*
或.*
运算符通过对象调用。这不仅仅是编译器对类型的挑剔。指向成员函数的指针甚至根本不是指针(或者至少不仅仅是指针)。它需要更多信息才能处理动态绑定(bind)。
有关更多详细信息,请参见http://www.parashift.com/c++-faq/pointers-to-members.html。
通过C风格的API使用指向成员函数的指针的“经典”方法是编写一个包装函数,该函数从其他地方(通常是“userinfo”,“thunk”等)与该函数一起传递,以获取
MyClass*
指针)并使用它调用myobj->*pmf(args)
。然后,您可以将指针传递给该包装器,因为它只是一个普通的函数。做到这一点的更智能的现代方法包括bind
和lambdas。首先让我们处理现代方法,因为它要简单得多,这是您应该做的。首先,如果您具有C++ 11,则可以使用
std::bind
或std::mem_fn
,或只编写一个lambda:auto pt2apply = [&](double *parameter) { return my_solver->pt2apply(parameter); }
现在,这个东西的类型不是
double(*)(double*)
。实际上,这是未指定的,而且可能是丑陋的。在这里,我们只是使用auto
来避免处理它,但是我们必须将其传递给set_pt2func
,然后该方法必须将其存储在成员变量中,因此我们需要某种可以键入的类型。答案是std::function<double(double *)>
。这个对象可以存储double *
可以调用的任何内容的副本,并返回double
。所以:void MySolver::set_pt2func(std::function<double(double*)> pt2function)
{
pt2Function = pt2function;
}
就是这样。
现在,这需要C++ 11。如果您使用的是C++ 98,则可以使用
boost::lambda
代替C++ 11 lambda(很丑陋,但基本思想相同)。或者,您可以使用boost::bind
(几乎与基于Boost库的C++ 11 std::bind
相同),或使用许多具有类似功能的其他库之一。并存储函子boost::function
(再次,与C++ 11 std::function
几乎相同,尽管有时效率较低)。 (如果您使用的是带有TR1的C++ 03,则std
或std::tr1
中可能具有某些C++ 11功能,但是直到C++ 11即将发布时,不同的编译器制造商才全部弄清细节。完成,因此最简单的使用方法是通过boost::tr1
,如果您拥有Boost,也可以使用它。)如果由于某种原因而坚持使用C++ 98而没有使用Boost,则可以使用
std::mem_fn
,并将结果存储在std::binary_function
中。这太丑了,所以在这里我不会给出完整的细节。现在让我们看一下经典的解决方案。仅当您已有一个使用函数指针(例如回调)的现有API并且无法更改它时,这才真正值得做。我建议寻找使用像
qsort_r
这样的C函数的示例代码,因为这个想法几乎是相同的:您将函数指针与“thunk”一起传递,并且两者始终在一起。 (现代的解决方案基本上都是将两件事绑定(bind)到一个对象中,而无需跟踪重击,就可以调用它,使代码更易于读写,并使编译器更容易捕获错误。 )这里的关键是您需要一个包装函数,一个普通的C样式函数,您可以将其作为函数指针。您的重击将成为
MyClass
对象的指针。在我的示例中,我将通过thunk作为void *
进行传递,因为尽管这显然不如(编写的代码更多,编译器捕获错误的机会更少),但这是您最常看到的内容在您需要处理的通用C API中。因此,将它们放在一起:
void MySolver::set_pt2func(double(*pt2function)(double*, void *), void *userinfo)
{
pt2Function = pt2function;
userInfo = userinfo;
}
double wrapMyClassPt2Apply(double *parameter, void *userinfo)
{
return ((MyClass*)userinfo)->apply_func(parameter);
}
my_solver->set_pt2func(wrapMyClassPt2Apply, (void *)my_solver);
请注意,如果您使用的是
pt2apply
成员函数指针,则可以在包装器中使用->*
进行调用。但是我们不再需要pt2apply
了;包装器可以只使用apply_func
调用->
成员。然后,当MySolver想要调用该函数时,它将执行以下操作:
pt2Function(parameter, userInfo);
最后一件事:wrapMyClassPt2Apply不能是成员函数,否则您将陷入与开始时同样的问题;指向它的指针将是指向成员函数的指针,而不是指针。因此,您有两种选择:
set_pt2func
调用并且已经是MyClass
的 friend (或子类)时,这才是有益的,因为您可以将方法设为私有(private)(或 protected )。 关于c++ - C++错误的参数类型-函数指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12635716/