通常情况下,除非你使用cast,否则你应该信任g++。
虽然你提到的函数类型都不能从C中导出使用,但这不是你要问的。你想知道可以作为函数指针传递的函数。
为了回答你能够传递什么,我认为更有建设性的方法是理解你不能传递什么。你不能传递需要在参数列表中未明确声明的其他参数的任何内容。
所以,没有非静态方法。它们需要一个隐式的“this”。C不会知道如何传递它。然而,编译器也不会让你这样做。
没有捕获lambda。它们需要一个隐含的参数和实际的lambda主体。
你可以传递不需要隐式上下文的函数指针。事实上,你已经列出了它们:
- 函数指针。无论是标准函数还是模板,只要模板被完全解析即可。这不是问题。你写的任何语法都将自动完全解析模板,从而得到函数指针。
- 非捕获lambda。这是C++11引入lambda时引入的一种特殊的解决方法。由于可能这样做,编译器执行所需的显式转换,使其发生。
- 静态方法。由于它们是静态的,所以它们不会传递隐式的"this",所以它们是可以的。
最后一个需要扩展。许多C回调机制得到一个函数指针和一个void* opaq。以下是一种标准且相当安全的使用C++类的方法:
class Something {
void callback() {
}
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 *)