C++ 移行ガイド

関数ポインタと void*

C では、「関数へのポインタ」と void* の間の暗黙的な変換は行われません。ARM で、「値が入れば」関数ポインタと void* の間で暗黙的な変換をする機能が追加され、C++ 4.2 でこの規則が実装されました。しかし、この暗黙的な変換は、予測できない関数の多重定義動作の原因となり、コードの移植性を損うため、その後 C++ から削除されました。さらに現在では、関数へのポインタと void* の間の暗黙的な変換も、キャストの場合を含め行われません。

C++ 5.0 は、互換モードでも標準モードでも、関数へのポインタと void* の間で暗黙的または明示的な変換が行われると警告を出します。C++ 5.0 は、どちらのモードでも、多重定義された関数呼び出しを解決するときにそのような暗黙的な変換を行いません。4.2 コンパイラに準拠しているそのようなコードは、5.0 コンパイラではエラー (一致する関数がない) になります。多重定義を適切に解決するために暗黙的な変換が必要な場合は、キャストを追加する必要があります。たとえば、次のようにします。


int g(int);
typedef void (*fptr)();
int f(void*);
int f(fptr);
void foo()
{
    f(g);          // この行は異なる動作になる
}

4.2 コンパイラでは、上記のコード例でコメントを付けた行は f(void*) を呼び出します。一方 5.0 コンパイラでは、一致する関数がないため、エラーメッセージが出されます。f((void*)g) のような明示的なキャストを追加することもできますが、コードが無効なため警告メッセージが出されます。コードは、関数ポインタと void* の間の変換が必要ないように修正すべきです。このようなコードは、変換が行われていたときでも移植性がありませんでした。

C++ には、void* に対応する「汎用関数ポインタ」はありません。C++ のすべての関数ポインタのサイズと表現は、サポートされるどのプラットフォームでも同じです。したがって、都合のよい任意の関数ポインタ型を使って関数ポインタの値を保持できます。この解決策は、多くのプラットフォームに対して移植性があります。従来と同じように、ポインタ値を使って関数を呼び出す場合は、ポインタ値を元の型に変換する必要があります。extern“C” 関数へのポインタ」を参照してください。