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

标签 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和再次兼容的类型显示编译错误。其他方式来处理我的问题?谢谢!

最佳答案

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

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::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 lambda(很丑陋,但基本思想相同)。或者,您可以使用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”一起传递,并且两者始终在一起。 (现代的解决方案基本上都是将两件事绑定(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不能是成员函数,否则您将陷入与开始时同样的问题;指向它的指针将是指向成员函数的指针,而不是指针。因此,您有两种选择:
  • 使其成为自由函数。这是完全合理的事情。自由函数可以是类接口(interface)的一部分。或者,它们可以比私有(private)方法更好地隐藏(通过将它们放在.cpp文件中,而根本不在.h文件中提及它们)。
  • 使其成为静态方法。有人喜欢这个,但实际上,只有在MyClass.cpp之外的某人将要进行set_pt2func调用并且已经是MyClass的 friend (或子类)时,这才是有益的,因为您可以将方法设为私有(private)(或 protected )。
  • 关于c++ - C++错误的参数类型-函数指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12635716/

    相关文章:

    c++ - 有没有一种方法可以使用 2 位大小的类型而不是 int,只需插入新的类型名称而不是 int?

    c++ - 在紧密循环中,单个 'if' 语句可以产生多少影响(时间)?

    c++ - 如何编写一个接受任何 std::chrono::duration (1ms, 4s, 7h) 并将秒数作为 float 的函数?

    c++ - 删除时无效指针崩溃,但指针不同

    c - 在 Xcode 6.1 中获取预期标识符或 '(' 问题

    javascript - 如何使用打字克隆 typescript 中的对象

    javascript - 注释 javascript 以进行静态类型检查的好解决方案是什么?

    c++ - 我将 QPixmap 传递给 QAbstractButton::setIcon 而不是 QIcon,但我没有收到错误...为什么?

    c++ - 如何提高 C++ 中的 std::set_intersection 性能?

    c - 二叉树和 doubleTree() 函数