我可以使用Lambda函数或std :: function对象代替函数指针吗?

13

我有一个需要使用的库,它定义了以下内容:

typedef void CallbackFunction(const int& i);

并且有一个注册回调函数的函数,它看起来像:

void registerCallback(CallbackFunction* pCallback);

因为我想要捕获多个变量的状态以便在回调函数中使用,所以我不能简单地使用普通函数。我更喜欢使用lambda函数,但以下代码无法编译:

auto fCallback = [](const int& i) {
    cout << i << endl;
};
registerCallback(fCallback);

但我得到了以下错误:

error C2664: 'registerCallback' : cannot convert parameter 1 from '`anonymous-namespace'::<lambda0>' to 'CallbackFunction (__cdecl *)'
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

我已经花了很多时间研究这个话题,尝试了一些不同的方法(可能很蠢),但似乎无法使其工作。将函数进行强制类型转换可以编译代码,但(毫不奇怪)它会崩溃。也许我在StackOverflow或其他地方忽略了解决方案,所以一个链接就足够了。(不过,因为我对某些技术还是有点新手,所以请确保对新手足够清晰。例如,如果此对话包含我的答案,我并不理解。请简化或解释相应内容。)顺便说一下,我正在使用Visual C++ 2010。

如果有什么需要澄清的,请让我知道。感谢提前帮助!

4个回答

8
一般情况下,您不能使用 lambda 表达式或函数对象来代替需要函数指针的位置。函数对象是完整的对象,已经重载了应用程序运算符 (()),因此您可以在语法上像使用函数一样使用它们。
标准算法是以模板形式编写的,这使您可以将函数地址(指针)或函数对象传递给它们,因为它们使用相同的语法。当您传递一个函数对象时,会实例化一个接受该函数对象的模板; 当您传递一个函数指针时,会实例化另一个模板。
因此,要使 lambda 表达式/函数对象与您的代码配合使用,您需要修改正在使用的库。

7
实际上这只是部分正确。Lambda表达式在C++0x中的工作方式是,当它们不捕获任何作用域时,具有隐式转换函数到函数指针。由于VS2010是在发布该编译器之后才推出此功能,因此它不支持实现此功能。 - Edward Strange
@Noah Roberts:这个定义在哪里? - Martin York

8

由于你没有捕获任何内容,所以你应该能够做你想做的事情,即将lambda表达式分配为函数指针(尽管你的语法有误)。

然而,由于你正在使用VS2010,所以你无法这样做。你试图使用lambda的特性在VS2010发布之后才出现:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3043.html

所以虽然它应该可以工作,但实际上却不能。

当然,虽然事实上你并没有捕获任何东西,但你确实声明了你想要捕获。在C++草案标准的后期,捕获数据的lambda表达式甚至也不能转换为函数指针。


谢谢你的信息,Noah!但是是的,我确实想要捕获数据,所以你说得对,即使它在VS2010中实现了,这个技巧也无法适用于我的情况。尽管如此,这绝对值得知道。 - Michael Repucci
1
他们有可能将具有捕获的lambda转换为函数指针的能力添加到语言中吗? - Fredrick

4

简短回答:不行

详细解答:

Lambda表达式只是一种函数对象的语法糖。
函数对象是像函数一样的对象。

函数指针并不是同一类型的东西。通常情况下,你不能在需要函数指针的地方使用函数对象。

额外信息:

另一方面,将一个函数包装成函数对象很容易。

历史上:

C库通常使用函数指针。而C++库将使用接口来实现相同的效果。因此,你可以看到为什么在需要函数指针的地方传递函数对象是困难的(C代码无法理解如何使用函数对象)。


1
将一个函数器包装在函数中需要回调传递一个可转换回函数器的任意指针。如果API没有提供这个功能,那就没办法了。 - Mark Ransom

0

你不能使用lambda表达式,因为你的registerCallback函数期望指向非成员函数的指针。传递额外参数到自由函数的唯一方法是1)使用全局(静态)数据或2)将其作为模板,采用编译时常量,例如:

template<int data> void callback(const int& i)
{ /* can use data */ }

registerCallback(callback<10>);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接