Language linkage
異なるプログラミング言語で書かれたプログラム単位間の連携を提供します。
|
これは宣言をそのモジュールから切り離すためにも使用できます。詳細は Module ownership を参照してください。 |
(since C++20) |
extern
string-literal
{
declaration-seq
(オプション)
}
|
(1) | ||||||||
extern
string-literal
declaration
|
(2) | ||||||||
| string-literal | - | 必要な言語リンケージを指定する 未評価文字列リテラル |
| declaration-seq | - | 宣言のシーケンス(ネストしたリンケージ指定を含む可能性あり) |
| declaration | - | 宣言 |
目次 |
説明
すべての関数型、 外部リンケージ を持つすべての関数名、 および 外部リンケージ を持つすべての変数名は、 言語リンケージ と呼ばれる特性を持ちます。言語リンケージは、 他のプログラミング言語で書かれたプログラム単位とリンクするために必要な一連の要件をカプセル化します: 呼出規約 、 名前マングリング (名前装飾)アルゴリズムなど。
保証されている言語リンケージは2つだけです:
- "C++" 、デフォルトの言語リンケージ。
- "C" 、Cプログラミング言語で書かれた関数とのリンクを可能にし、Cで書かれたユニットから呼び出せる関数をC++プログラム内で定義することを可能にするもの。
extern "C" { int open(const char *path_name, int flags); // C関数の宣言 } int main() { int fd = open("test.txt", 0); // C++プログラムからC関数を呼び出す } // このC++関数はCコードから呼び出し可能 extern "C" void handler(int) { std::cout << "Callback invoked\n"; // C++を使用可能 }
言語リンケージはすべての関数型の一部であるため、関数へのポインタも言語リンケージを保持します。関数型の言語リンケージ(呼出規約を表す)と関数名の言語リンケージ(名前修飾を表す)は互いに独立しています:
extern "C" void f1(void(*pf)()); // Cリンケージを持つ関数f1を宣言 // 戻り値void、引数としてC関数へのポインタを受け取る // そのC関数は戻り値void、パラメータなし extern "C" typedef void FUNC(); // FUNCをC関数型として宣言 // 戻り値void、パラメータなし FUNC f2; // 名前f2はC++リンケージを持つが、その型はC関数 extern "C" FUNC f3; // 名前f3はCリンケージを持ち、その型はC関数void() void (*pf2)(FUNC*); // 名前pf2はC++リンケージを持ち、その型は // 「C++関数へのポインタで、戻り値void、引数として // 'C関数へのポインタ(戻り値void、パラメータなし)'を1つ受け取る」 extern "C" { static void f4(); // 関数f4の名前は内部リンケージ(言語指定なし) // ただし関数の型はC言語リンケージを持つ }
あるエンティティの 2つの宣言 が異なる言語リンケージを指定する場合、プログラムは不適格となる。両方の宣言が互いに到達可能でない場合、診断は要求されない。リンケージ指定のないエンティティの再宣言は、そのエンティティ(およびその型が存在する場合は型)の言語リンケージを継承する。
extern "C" int f(); extern "C++" int f(); // エラー: 異なる言語リンケージ extern "C" int g(); int g(); // OK、C言語リンケージを持つ int h(); // デフォルトでC++言語リンケージを持つ extern "C" int h(); // エラー: 異なる言語リンケージ
"C"リンケージの特別な規則
クラスメンバ、 トレーリング requires 節を持つフレンド関数 、 (C++20以降) または非静的メンバ関数が "C" 言語ブロック内に現れる場合、それらの型のリンケージは "C++" のまま維持される(ただし、パラメータ型がある場合は "C" のまま維持される):
extern "C" { class X { void mf(); // 関数mfとその型はC++言語リンケージを持つ void mf2(void(*)()); // 関数mf2はC++言語リンケージを持つ // パラメータは「C関数へのポインタ」型を持つ }; } template<typename T> struct A { struct B; }; extern "C" { template<typename T> struct A<T>::B { friend void f(B*) requires true {} // C言語リンケージは無視される }; } namespace Q { extern "C" void f(); // 不適格ではない }
C
を、関数または変数を
"C"
言語リンケージで宣言する宣言とする。別の宣言
D
が同じ名前のエンティティを宣言し、以下のいずれかの条件を満たす場合、
C
と
D
は同じエンティティを宣言する:
-
Dはグローバルスコープに属する変数を宣言します。 -
Cが変数を宣言する場合、Dも変数を宣言します。 -
Cが関数を宣言する場合、Dも関数を宣言します。
通常の再宣言とは異なり、
C
と
D
は異なる
ターゲットスコープ
を持つことができます
:
extern "C" { int x; int f(); int g() { return 1; } } namespace A { int x; // エラー: 「x」を再定義しています int f(); // OK: 「f」を再宣言しています int g() { return 1; } // エラー: 「g」を再定義しています }
ただし、そのような宣言の restrictions は依然として適用され、両方が関数を宣言するか、両方が変数を宣言する必要があり、宣言されるエンティティは同じ型でなければなりません:
namespace A { extern "C" int x(); extern "C" int y(); } int x; // エラー: 「x」を異なる種類のエンティティとして再宣言 namespace B { void y(); // エラー: 「y」を異なる型で再宣言 }
注記
言語仕様は namespace scope でのみ現れます。
言語仕様の波括弧はスコープを確立しません。
言語仕様がネストする場合、最も内側の仕様が有効になります。
言語リンケージ指定内に直接含まれる宣言は、宣言された名前の extern 指定子 が含まれているものとして扱われ、 リンケージ の決定およびそれが 定義 であるかどうかの判断が行われます。
extern "C" int x; // 宣言であり定義ではない // 上記の行は extern "C" { extern int x; } と同等 extern "C" { int x; } // 宣言かつ定義 extern "C" double f(); static double f(); // エラー: リンケージの競合 extern "C" static void g(); // エラー: リンケージの競合
extern "C" を使用することで、C++プログラムにCライブラリ関数の宣言を含むヘッダファイルをインクルードすることが可能になります。しかし、同じヘッダファイルをCプログラムと共有する場合、 extern "C" (これはCでは許可されていません)は適切な #ifdef で隠す必要があります。通常は __cplusplus を使用します:
#ifdef __cplusplus extern "C" int foo(int, int); // C++コンパイラがこれを認識 #else int foo(int, int); // Cコンパイラがこれを認識 #endif
"C"と"C++"の言語リンケージのみが異なる関数型を区別する唯一の現代的なコンパイラはOracle Studioであり、他のコンパイラ(GCC、Clangなど)は言語リンケージのみが異なるオーバーロードを許可していません。これにはC++標準で要求されているオーバーロードセット( std::qsort 、 std::bsearch 、 std::signal 、 std::atexit 、および std::at_quick_exit )も含まれます: GCC bug 2316 、 Clang bug 6277 、 CWG issue 1555 。
extern "C" using c_predfun = int(const void*, const void*); extern "C++" using cpp_predfun = int(const void*, const void*); // 不適格だが、ほとんどのコンパイラで受理される static_assert(std::is_same<c_predfun, cpp_predfun>::value, "CとC++の言語リンケージは関数型を区別すべきではない。"); // 以下の宣言はほとんどのコンパイラでオーバーロードとして宣言されない // c_predfunとcpp_predfunが同じ型と見なされるため void qsort(void* base, std::size_t nmemb, std::size_t size, c_predfun* compar); void qsort(void* base, std::size_t nmemb, std::size_t size, cpp_predfun* compar);
キーワード
不具合報告
以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。
| DR | 適用対象 | 公開時の動作 | 正しい動作 |
|---|---|---|---|
| CWG 4 | C++98 | 内部リンケージを持つ名前は言語リンケージを持つことができる | 外部リンケージを持つ名前に限定される |
| CWG 341 | C++98 |
"C"
言語リンケージを持つ関数は
グローバル変数と同じ名前を持つことができる |
この場合プログラムは不適格
(異なる翻訳単位に現れる場合は 診断不要) |
| CWG 564 | C++98 |
2つの宣言が言語リンケージ指定のみが異なる場合
(すなわち 'extern' に続く文字列リテラルが異なる場合)、 プログラムは不適格であった |
代わりに宣言によって与えられる実際の
言語リンケージが比較される |
| CWG 2460 | C++20 |
末尾に
requires
節を持つフレンド関数と
"C" 言語リンケージが競合動作していた |
この場合
"C"
言語リンケージ
は無視される |
| CWG 2483 | C++98 |
"C"
言語ブロック内に現れる静的メンバ関数の
型のリンケージは "C++" であった |
リンケージは "C" である |
参考文献
- C++23規格 (ISO/IEC 14882:2024):
-
- 9.11 リンケージ指定 [dcl.link]
- C++20標準 (ISO/IEC 14882:2020):
-
- 9.11 リンケージ仕様 [dcl.link]
- C++17 標準 (ISO/IEC 14882:2017):
-
- 10.5 リンケージ仕様 [dcl.link]
- C++14 標準 (ISO/IEC 14882:2014):
-
- 7.5 リンケージ指定 [dcl.link]
- C++11標準 (ISO/IEC 14882:2011):
-
- 7.5 リンケージ指定 [dcl.link]
- C++03標準 (ISO/IEC 14882:2003):
-
- 7.5 リンケージ指定 [dcl.link]
- C++98 標準 (ISO/IEC 14882:1998):
-
- 7.5 リンケージ指定 [dcl.link]