一般来说,除非你使用 cast,否则你应该信任 g++。
虽然您提到的任何函数类型都不能导出以在 C 中使用,但这不是您要问的。您在询问可以作为函数指针传递的函数。
要回答你能通过什么,我认为理解你不能通过什么更有建设性。您不能传递任何需要参数列表中未明确说明的附加参数的内容。
所以,没有非静态方法。他们需要一个隐含的“this”。C不会知道通过它。再说一次,编译器不会让你。
没有捕获 lambdas。它们需要一个带有实际 lambda 主体的隐式参数。
您可以传递的是不需要隐式上下文的函数指针。事实上,您继续列出它们:
- 函数指针。无论是标准函数还是模板都没有关系,只要模板完全解析即可。这不是问题。您编写的任何导致函数指针的语法都将自动完全解析模板。
- 非捕获 lambdas。这是 C++11 在引入 lambda 时引入的一种特殊解决方法。由于可以这样做,因此编译器会进行显式转换以使其发生。
- 静态方法。由于它们是静态的,它们不会通过隐式传递
this
,所以它们没问题。
最后一个值得扩展。许多 C 回调机制得到一个函数指针和一个 void* opaq。以下是在 C++ 类中使用它们的标准且相当安全:
class Something {
void callback() {
// Body goes here
}
static void exported_callback(void *opaq) {
static_cast<Something*>(opaq)->callback();
}
}
然后做:
Something something;
register_callback(Something::exported_callback, &something);
编辑添加:
这有效的唯一原因是,当没有传递隐式参数时,C++ 调用约定和 C 调用约定是相同的。名称修饰存在差异,但在传递函数指针时并不相关,因为名称修饰的唯一目的是允许链接器找到正确的函数地址。
如果您尝试使用期望回调的技巧,例如 stdcall 或 pascal 调用约定,那么该方案将一蹶不振。
然而,这并不是静态方法、lambda 和模板函数所独有的。在这种情况下,即使是标准功能也会失败。
可悲的是,当您定义一个指向 stdcall 类型的函数指针时,gcc 会忽略您:
#define stdcall __attribute__((stdcall))
typedef stdcall void (*callback_type)(void *);
结果是:
test.cpp:2:45: warning: ‘stdcall’ attribute ignored [-Wattributes]
typedef stdcall void (*callback_type)(void *);