我有一个函数,它将一个指向函数的参数作为参数:
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
,再次兼容类型。处理我的问题的其他方法?谢谢!
答案 0 :(得分:5)
你需要一个指向函数的指针:
double(*)(double*)
你所拥有的是指向成员的指针功能:
double (MyClass::*)(double*)
这些不是一回事。甚至是所有类似的。第一个可以直接调用;只能使用->*
或.*
运算符通过对象调用第二个。
这不仅仅是编译器对类型的挑剔;指向成员函数的指针甚至不是指针下的指针(或者至少不是只是指针);它需要更多信息才能处理动态绑定。
有关详细信息,请参阅http://www.parashift.com/c++-faq/pointers-to-members.html。
使用带有C风格API的指针到成员函数的“经典”方法是编写一个从其他地方获取MyClass*
的包装函数(通常是“userinfo”,“thunk”,等等与函数指针一起传递)并用它调用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 lambdas(很多丑陋,但基本的想法相同)。或者您可以使用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”一起传递,并且它们中的两个总是一起旅行。 (现代解决方案基本上都是将两个东西绑定到一个对象中,您可以调用它而无需跟踪thunk,使代码更易于读写,并且编译器更容易捕获错误。 )
这里的关键是你需要一个包装函数,一个普通的C风格的函数,你可以使用函数指针。你的thunk将成为你MyClass
对象的指针。在我的例子中,我将以void *
的方式传递thunk,因为即使这显然是次等的(编写的代码越多,编译器捕获错误的机会越来越少),这就是你要去的东西最常见的是你需要处理的通用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不能成为成员函数,否则你会遇到与之相同的问题;指向它的指针将是指向成员函数的指针而不是指针。所以,你有两个选择:
使其成为免费功能。这是一件非常合理的事情。自由函数可以是类的接口的一部分。或者,它们可以隐藏得比私有方法更好(通过将它们放在.cpp文件中而不在.h文件中提及它们。)
将其设为静态方法。有些人更喜欢这个,但实际上,只有当MyClass.cpp之外的人要进行set_pt2func
调用并且已经是MyClass
的朋友(或子类)时,它才有用。因为那样你就可以把方法设为私有(或受保护)。
答案 1 :(得分:1)
没有简单的解决方案。指向成员函数的指针是不同的,因为它们需要调用对象。 MySolver
如何提供MyClass
?基本上,一旦你得到答案,潜在的问题基本消失了。 MySolver
知道MyClass
的两个常见解决方案,在这种情况下,您可以修复set_pt2func的签名,或者为MySolver
提供std::bind(pt2apply, SomeObj, _1)
的结果。
答案 2 :(得分:0)
指向成员函数的指针与常规函数(c样式函数指针)不兼容。这是因为成员采用不属于签名的隐式this
参数。如果你想实现类似的行为,请看一下boost :: bind。
答案 3 :(得分:-1)
使用C ++ 11而不是C.这意味着:使用std::function
和std:mem_fn
来绑定成员函数。