C++ 移行ガイド

言語リンケージ

「言語リンケージ」とは、関数の呼び出しに関する方法を意味します。たとえば、引数の場所、戻り値の検出場所の指定などがこれに当たります。言語リンケージを宣言するということは、その言語で関数が記述されないという意味です。言語リンケージを宣言すると、指定した言語で記述されているかのように関数を呼び出すことができます。つまり、C++ 関数が C リンケージを持つように宣言するとは、C 言語で記述された関数から C++ 関数を呼び出せるようにするということです。

関数の宣言に適用された言語リンケージは、戻り値型、および関数または関数へのポインタを持つすべてのパラメータに適用されます。

C++ 4.2 コンパイラでは、言語リンケージは関数の型の構成要素ではないという、ARM の規則が実装されています。特に、ポインタのリンケージや割り当てられた関数とは無関係に、関数へのポインタを宣言することができます。C++ 5.0 コンパイラの互換モードでは、これと同じ規則が適用されています。

C++ 5.0 の標準モードでは、言語リンケージはその関数の型の構成要素であり、かつ、関数へのポインタの型の構成要素であるという新しい規則が実装されています。このため、リンケージは関数とポインタとの間で一致していなければなりません。

次の例は、C リンケージと C++ リンケージを持つ関数および関数へのポインタの組み合わせとして考えられる 4 つの場合すべてを表しています。4.2 コンパイラおよび 5.0 コンパイラを互換モードで使用すると、あらゆる組み合わせが受け入れられます。標準モードの 5.0 コンパイラでは、一致していない組み合わせは旧式とみなされます。


extern“C”int fc(int) { return 1; }      // fcの C リンケージ
int fcpp(int) { return 1; }               // fcppの C++ リンケージ
// fp1 と fp2 の C++ リンケージ
int (*fp1)(int) = fc;                     //不一致
int (*fp2)(int) = fcpp;                   // OK 
// fp3 と fp4 の C リンケージ
extern“C”int (*fp3)(int) = fc;          // OK 
extern“C”int (*fp4)(int) = fcpp;        //不一致

リンケージに関連して問題が発生した場合は、C リンケージ関数と組み合わせ可能なポインタが C リンケージで宣言され、C++ リンケージ関数と組み合わせられるポインタにリンケージ指定がないか、または、C++ リンケージで宣言されていることを確認してください。


extern“C”{
    int fc(int);
    int (*fp1)(int) = fc; // どちらも C リンケージを持つ
}
int fcpp(int); 
int (*fp2)(int) = fcpp;   // どちらも C++ リンケージを持つ

ポインタと関数が一致しない場合は、関数を包含するコード (ラッパー) を記述することによって、コンパイラのエラーを回避することができます (Solaris では、C と C++ の関数リンケージは同じですが、一般的には、リンケージが一致していないとエラーになります。これは新しい言語規則として適用されているためです)。

次の例では、 composer は、C リンケージを持つ関数へのポインタを取る C 関数です。


extern“C”void composer( int(*)(int) );
extern“C++”int foo(int); 
composer( foo ); // 不一致

関数 foo (C++ リンケージを持つ) を 関数 composer に渡すには、次のように foo に C インタフェースを提供する foo_wrapper という C リンケージ関数を作成します。


extern“C”void composer( int(*)(int) );
extern“C++”int foo(int);
extern“C”int foo_wrapper(int i) { return foo(i); }
composer( foo_wrapper ); // OK

この手法は、コンパイラのエラーを回避するためだけでなく、C と C++ の関数が実際には異なるリンケージを持っている場合にも使用できます。