c++错误的参数类型-函数指针

c++ wrong argument type - function pointer

本文关键字:函数 指针 类型 参数 错误 c++      更新时间:2023-10-16

我有一个函数,它将指向函数的指针作为参数:

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-mmfn.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*)

这不是一回事。甚至所有类似的东西。第一个可以直接调用;第二个只能通过具有->*.*运算符的对象调用。

这不仅仅是编译器对类型的挑剔;指向成员函数的指针在本质上甚至不是指针(或者至少不是,只是指针);它需要更多的信息来处理动态绑定。

请参阅http://www.parashift.com/c++-faq/pointers-to-members.html了解更多详细信息。

在C风格API中使用指向成员函数的指针的"经典"方法是编写一个包装函数,该包装函数从其他地方获取MyClass*(通常是与函数指针一起传递的"userinfo"、"thunk"等),并用它调用myobj->*pmf(args)。然后,您可以将指针传递给该包装器,因为它只是一个普通函数。更聪明的现代方法包括bind和lambdas。

让我们先处理一下现代的方式,因为它要简单得多,如果可以的话,这就是你应该做的。首先,如果您有C++11,您可以使用std::bindstd::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(与C++11std::bind几乎相同,后者基于Boost库),或者使用许多其他具有类似功能的库之一。为了存储函子boost::function(同样,与C++11std::function几乎相同,尽管有时效率较低)。(如果你有带TR1的C++03,你可能在stdstd::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对象的指针。在我的例子中,我将把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;包装器可以只调用具有CCD_ 32的CCD_。

然后,当MySSolver想要调用函数时,它会执行以下操作:

pt2Function(parameter, userInfo);

最后一件事是:wrapMyClassPt2Apply不能是成员函数,否则您将陷入与刚开始时相同的问题;指向它的指针将是指向成员函数的指针,而不是指针。所以,你有两个选择:

  1. 使其成为一个免费函数。这是一个非常合理的做法。自由函数可以是类接口的一部分。或者,它们可以比私有方法隐藏得更好(将它们放在.cpp文件中,而在.h文件中根本不提及它们)。

  2. 使其成为静态方法。有些人更喜欢这样,但实际上,只有当MyClass.cpp之外的人要进行set_pt2func调用,并且已经是MyClass的朋友(或子类)时,这才是有益的,因为这样你就可以将该方法设为私有(或受保护)。

没有简单的解决方案。指向成员函数的指针是不同的,因为它们需要调用一个对象。MySolver将如何提供MyClass?基本上,一旦你有了答案,基本问题就基本上消失了。两种常见的解决方案是MySolver知道MyClass,在这种情况下,您可以修复set_pt2func的签名,或者向MySolver提供std::bind(pt2apply, SomeObj, _1)的结果。

指向成员函数的指针与常规指针(c样式函数指针)不兼容。这是因为成员采用了一个不属于签名的隐式this参数。如果您想实现类似的行为,请查看boost::bind。

使用C++11而不是C。这意味着:使用std::functionstd:mem_fn绑定成员函数。