一般に、キャストを使用しない限り、g++ を信頼する必要があります。
あなたが言及した関数型のどれもC内から使用するためにエクスポートできないことは事実ですが、これはあなたが求めているものではありません. 関数ポインタとして渡すことができる関数について尋ねています。
合格できることを答えるには、合格できないことを理解する方が建設的だと思います。引数リストに明示的に記載されていない追加の引数を必要とするものを渡すことはできません。
したがって、非静的メソッドはありません。暗黙の「これ」が必要です。Cはそれを渡すことを知りません。繰り返しますが、コンパイラは許可しません。
ラムダをキャプチャしません。それらは、実際のラムダ本体で暗黙の引数を必要とします。
渡すことができるのは、暗黙のコンテキストを必要としない関数ポインターです。実際のところ、あなたは先に進んでそれらをリストしました:
- 関数ポインタ。テンプレートが完全に解決されている限り、それが標準関数であるかテンプレートであるかは関係ありません。これは問題ではありません。関数ポインターになる構文を作成すると、テンプレートが自動的に完全に解決されます。
- 非キャプチャ ラムダ。これは、C++11 がラムダを導入したときに導入された特別な回避策です。そうすることが可能であるため、コンパイラはそれを実現するために必要な明示的な変換を行います。
- 静的メソッド。それらは静的である
this
ため、暗黙的な が渡されないため、問題ありません。
最後の 1 つは拡張に耐えます。多くの 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 呼び出し規則などを期待するコールバックでそのトリックを試した場合、このスキームは表面上失敗するでしょう。
ただし、これは静的メソッド、ラムダ、およびテンプレート関数に固有のものではありません。そのような状況では、標準関数でさえ失敗します。
悲しいことに、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 *);