C++ 移行ガイド

関数のパラメータとしての関数へのポインタ

言語リンケージに関する新しい規則の追加に伴う微妙な問題があります。それは、上記の例の composer 関数のような、パラメータとして関数へのポインタを取る関数の問題です。


extern“C”void composer( int(*)(int) );

言語リンケージに関する規則のうち、変更されていない規則として、言語リンケージを持つ関数が宣言されていて、その後に「同じ関数」が言語リンケージなしで定義されている場合は、前の言語リンケージが適用されるという規則があります。


extern“C”int f(int);
int f(int i) { ... } // “C”リンケージを持つ

上記の関数 f は C リンケージを持ちます。この宣言 (インクルードされるヘッダーファイルに含まれている可能性もある) の後の定義は、リンケージ指定を継承します。しかし、次の例に示すように、この関数が関数へのポインタ型のパラメータを取る場合はどうなるのでしょう。


extern“C”int g( int(*)(int) );
int g( int(*pf)(int) ) { ... } //“C”または“C++”リンケージのどちらか?

古い規則と 4.2 コンパイラでは、このコードには、g という関数が 1 つ存在するだけです。新しい規則では、1 行目は、C リンケージを持つ関数へのポインタを取る、C リンケージを持つ関数 g を宣言し、2 行目は、C++ リンケージを持つ関数へのポインタを取る関数を定義していることになります。2 つの関数は同じではありません。2 つ目の関数は C++ リンケージを持ちます。リンケージは関数へのポインタの型の構成要素であるため、2 つの行は、それぞれが g という名前の多重定義関数を参照します。このため、これらの関数が同じ関数であることに依存するコードは、問題になります。コンパイルまたはリンクが失敗する可能性が非常に高くなります。

プログラミングするときの習慣として、リンケージは、宣言だけでなく、関数の定義でも指定するようにしてください。


extern“C”int g( int(*)(int) );
extern“C”int g( int(*pf)(int) ) { ... }

型に関する混乱は、関数パラメータに typedef を使用することでさらに少なくすることができます。


extern“C”typedef int (*pfc)(int); // Cリンケージ関数へのポインタ
extern“C”int g(pfc);
extern“C”int g(pfc pf) { ... }