1

我有一个函数,它以函数指针为参数:

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

4 回答 4

6

您需要一个指向函数的指针:

double(*)(double*)

你所拥有的是一个指向成员函数的指针:

double (MyClass::*)(double*)

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

这不仅仅是编译器对类型的挑剔。指向成员函数的指针甚至不是一个指针(或至少不仅仅是一个指针)。它需要更多信息才能处理动态绑定。

有关详细信息,请参阅http://www.parashift.com/c++-faq/pointers-to-members.html 。

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

让我们先处理现代方式,因为它更简单,如果可以的话,这是你应该做的。首先,如果你有 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 *)>。这是一个对象,可以存储任何可以用 a 调用double *并返回 a的副本double。所以:

void MySolver::set_pt2func(std::function<double(double*)> pt2function)
{
    pt2Function = pt2function;
}   

就是这样。

现在,这需要 C++11。如果您使用的是 C++98,则可以使用boost::lambdaC++11 的 lambda 代替(丑得多,但基本思想相同)。或者您可以使用(几乎与基于 Boost 库的boost::bindC++11 相同)或具有类似功能的许多其他库之一。std::bind并存储仿函数boost::function(同样,几乎与 C++11 相同std::function,尽管有时效率较低)。(如果你有带有 TR1 的 C++03,你可能在stdor中有一些 C++11 的特性std::tr1,但是不同的编译器制造商直到 C++11 几乎完成之前都没有理顺细节,所以最简单的使用方法是通过boost::tr1,如果你有 Boost,你不妨直接使用它。)

如果由于某种原因你被 C++98 卡住并且没有 Boost,你可以使用std::mem_fn, 并将结果存储在std::binary_function. 这更难看,所以我不会在这里给出完整的细节。

现在让我们看看经典的解决方案。只有当您有一个现有的 API 使用函数指针(例如,回调)并且您无法更改它时,才真正值得这样做。我建议寻找使用 C 函数的示例代码,例如qsort_r,因为这个想法几乎相同:您将函数指针与“thunk”一起传递,并且它们两者总是一起旅行。(现代解决方案基本上都是关于将这两个东西绑定到一个对象中,您可以调用该对象而无需跟踪 thunk,以使您的代码更易于阅读和编写,并使编译器更容易捕获错误。 )

这里的关键是您需要一个包装函数,一个可以获取函数指针的普通 C 风格函数。您的 thunk 将是指向您的MyClass对象的指针。在我的示例中,我将把 thunk 作为 a 传递,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 不能是成员函数,否则您会遇到与开始时相同的问题;指向它的指针将是指向成员函数的指针,而不是指针。所以,你有两个选择:

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

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

于 2012-09-28T07:52:03.860 回答
1

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

于 2012-09-28T07:46:50.357 回答
0

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

于 2012-09-28T07:46:16.643 回答
-1

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

于 2012-09-28T08:20:39.613 回答