我在C ++ 11项目中使用C库,这个C库提供了一个需要函数指针的函数。除非我捕获变量,否则我想将C ++ 11 lambda传递给它,它正常工作。这是一个简短的例子:
#include <cstdio>
#include <functional>
typedef int (*Callback)();
void legacy(Callback callback) {
printf("%i\n", callback());
}
int stdCallback() {
return 1;
}
int main(int argc, char* argv[]) {
int number = 3;
// Standard C callback works
legacy(stdCallback);
// Lambda without capturing works
legacy([]() { return 2; });
// Lambda with capturing doesn't work
legacy([&]() { return number; });
return 0;
}
GNU C ++编译器在第三次调用legacy
函数时给出了以下错误消息:
test.cpp: In function ‘int main(int, char**)’:
test.cpp:24:36: error: cannot convert ‘main(int, char**)::<lambda()>’ to ‘Callback {aka int (*)()}’ for argument ‘1’ to ‘void legacy(Callback)’
legacy([&]() { return number; });
我该如何解决这个问题?或者技术上不可能使用捕获lambda作为C函数指针?
答案 0 :(得分:9)
不,如果它捕获了任何东西,你就无法将lamdba转换为函数指针。
C ++标准,第5.1.2 / 6节:[expr.prim.lambda],强调我的:
非通用lambda表达式没有lambda-capture的闭包类型具有公共非虚拟非显式const转换函数,指向函数,具有C ++语言链接(7.5) )具有与闭包类型的函数调用操作符相同的参数和返回类型。此转换函数返回的值应为函数的地址,该函数在调用时与调用闭包类型的函数调用操作符具有相同的效果
答案 1 :(得分:5)
函数指针就是:指向某些代码位的指针。没有数据。
带有捕获的lambda也需要捕获的数据才能产生有用的结果。
没有办法可以自动运作。但是,根据回调函数的调用方式,您可以解决它。您的legacy
函数不会将回调保存在任何位置,它只会在自己执行期间调用它。此外,您还没有使用线程或类似的东西。只要这些假设成立,您就可以解决问题:
#include <cstdio>
#include <functional>
typedef int (*Callback)();
void legacy(Callback callback) {
printf("%i\n", callback());
}
// Declared as void * outside legacyWrapper, instead of T * inside legacyWrapper,
// because no more than one variable is needed, no matter how many instantiations of
// legacyWrapper there are.
static void *legacyWrapperCallback;
template <typename T>
void legacyWrapper(T callback) {
legacyWrapperCallback = &callback;
legacy([]() -> int { return (*static_cast<T *>(legacyWrapperCallback))(); });
}
int stdCallback() {
return 1;
}
int main(int argc, char* argv[]) {
int number = 3;
// Standard C callback works
legacy(stdCallback);
// Lambda without capturing works
legacy([]() { return 2; });
// Lambda with capturing doesn't work...
//legacy([&]() { return number; });
// ...unless the C++ wrapper is used
legacyWrapper([&]() { return number; });
return 0;
}