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

时间:2012-09-28 07:42:22

标签: c++ pointers types compiler-errors function-pointers

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

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,再次兼容类型。处理我的问题的其他方法?谢谢!

4 个答案:

答案 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::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(几乎与基于Boost库的C ++ 11 std::bind相同),或者使用具有类似功能的许多其他库之一。并存储仿函数boost::function(再次,几乎与C ++ 11 std::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对象的指针。在我的例子中,我将以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不能成为成员函数,否则你会遇到与之相同的问题;指向它的指针将是指向成员函数的指针而不是指针。所以,你有两个选择:

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

  2. 将其设为静态方法。有些人更喜欢这个,但实际上,只有当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::functionstd:mem_fn来绑定成员函数。