语言链接
提供以不同程序设计语言书写的程序单元间的链接。
这也可以用于将声明与其模块分离。见模块所有权。 |
(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]