語言連結
提供以不同程式設計語言書寫的程式單元間的連結。
這也可以用於將宣告與其模組分離。見模組所有權。 |
(C++20 起) |
extern 字串字面量 { 宣告序列 (可選) } |
(1) | ||||||||
extern 字串字面量 宣告 |
(2) | ||||||||
字串字面量 | - | 一個不求值字串字面量,指明所需的語言連結 |
宣告序列 | - | 一個宣告序列,可以包含巢狀的連結規範 |
宣告 | - | 一個宣告 |
目錄 |
[編輯] 解釋
每個函式型別、每個具有外部連結的函式名,以及每個具有外部連結的變數名,都擁有一個稱為語言連結(language linkage)的屬性。語言連結封裝了與另一種程式設計語言所編寫的程式單元進行連結所必需的一系列要求:呼叫約定、名字修飾(name decoration)演算法等。
保證只支援兩種語言連結
- "C++",預設語言連結。
- "C",使得能與以 C 程式設計語言寫成的函式連結,以及在 C++ 程式中定義能從以 C 寫成的單元呼叫的函式。
extern "C" { int open(const char *path_name, int flags); // C function declaration } int main() { int fd = open("test.txt", 0); // calls a C function from a C++ program } // This C++ function can be called from C code extern "C" void handler(int) { std::cout << "Callback invoked\n"; // It can use C++ }
因為語言連結是每個函式型別的一部分,所以函式指標也保持其語言連結。函式型別的語言連結(表示呼叫約定)和函式名的語言連結(表示名字修飾)是相互獨立的。
extern "C" void f1(void(*pf)()); // declares a function f1 with C linkage, // which returns void and takes a pointer to a C function // which returns void and takes no parameters extern "C" typedef void FUNC(); // declares FUNC as a C function type that returns void // and takes no parameters FUNC f2; // the name f2 has C++ linkage, but its type is C function extern "C" FUNC f3; // the name f3 has C linkage and its type is C function void() void (*pf2)(FUNC*); // the name pf2 has C++ linkage, and its type is // "pointer to a C++ function which returns void and takes one // argument of type 'pointer to the C function which returns void // and takes no parameters'" extern "C" { static void f4(); // the name of the function f4 has internal linkage (no language) // but the function's type has C language linkage }
如果某個實體的兩個宣告為它指定了不同的語言連結,那麼程式非良構;如果兩個宣告都不能從對方抵達,則不要求診斷。不帶連結規範的實體再次宣告會繼承該實體及其型別(若存在)的語言連結。
extern "C" int f(); extern "C++" int f(); // Error: different language linkages extern "C" int g(); int g(); // OK, has C language linkage int h(); // has C++ language linkage by default extern "C" int h(); // Error: different language linkages
[編輯] 對於 "C" 連結的特殊規則
當類成員、帶尾隨 requires 子句的友元函式(C++20 起)或非靜態成員函數出現於 "C" 語言塊中時,其型別的連結仍然是 "C++"(但形參型別若存在則仍為 "C")。
extern "C" { class X { void mf(); // the function mf and its type have C++ language linkage void mf2(void(*)()); // the function mf2 has C++ language linkage; // the parameter has type “pointer to C function” }; } template<typename T> struct A { struct B; }; extern "C" { template<typename T> struct A<T>::B { friend void f(B*) requires true {} // C language linkage ignored }; } namespace Q { extern "C" void f(); // not ill-formed }
設 C
是一個聲明瞭具有 "C" 語言連結的函式或變數的宣告。如果另一個宣告 D
聲明瞭一個同名的實體,並且滿足以下任一條件,則 C
和 D
聲明瞭同一個實體:
-
D
聲明瞭一個屬於全域性作用域的變數。 - 如果
C
聲明瞭一個變數,則D
也聲明瞭一個變數。 - 如果
C
聲明瞭一個函式,則D
也聲明瞭一個函式。
extern "C" { int x; int f(); int g() { return 1; } } namespace A { int x; // Error: redefines “x” int f(); // OK, redeclares “f” int g() { return 1; } // Error: redefines “g” }
然而,此類宣告的限制仍然適用,這意味著它們要麼都宣告函式,要麼都宣告變數,並且所宣告的實體必須具有相同的型別。
namespace A { extern "C" int x(); extern "C" int y(); } int x; // Error: redeclares “x” as a different kind of entity namespace B { void y(); // Error: redeclares “y” with a different type }
[編輯] 注意
語言規範只能出現於名稱空間作用域。
語言規範的花括號不建立作用域。
當語言規範巢狀時,最內層的規範生效。
為了確定所宣告的名字的連結以及它是否為定義,直接包含於語言連結規範中的宣告被當做它含有 extern 說明符那樣處理。
extern "C" int x; // a declaration and not a definition // The above line is equivalent to extern "C" { extern int x; } extern "C" { int x; } // a declaration and definition extern "C" double f(); static double f(); // error: linkage conflict extern "C" static void g(); // error: linkage conflict
extern "C" 使得 C++ 程式能包含聲明瞭 C 庫函式的標頭檔案,但如果同一標頭檔案與 C 程式共享,那麼必須用適當的 #ifdef,通常是 __cplusplus,來隱藏 extern "C"(它在 C 中是不允許的)。
#ifdef __cplusplus extern "C" int foo(int, int); // C++ compiler sees this #else int foo(int, int); // C compiler sees this #endif
唯一區分 "C" 和 "C++" 語言連結的函式型別的現代編譯器是 Oracle Studio,其他編譯器不允許僅在語言連結上有所區別的過載,包括 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*); // ill-formed, but accepted by most compilers static_assert(std::is_same<c_predfun, cpp_predfun>::value, "C and C++ language linkages shall not differentiate function types."); // following declarations do not declare overloads in most compilers // because c_predfun and cpp_predfun are considered to be the same type 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++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 4 | C++98 | 具有內部連結的名字可以有語言連結 | 限定為具有外部連結的名字 |
CWG 341 | C++98 | 具有 "C" 語言連結的函式可以 與全域性變數同名 |
在這種情況下程式格式錯誤 (若它們出現於不同翻譯單元則 不要求診斷) |
CWG 564 | C++98 | 若兩個宣告僅在語言連結規範上 有所不同(即 '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]