语言连接
提供在不同编程语言编写的程序单元之间进行连接的功能。
这也可以用于将声明从其模块中分离。请参阅模块所有权。 |
(自 C++20 起) |
extern 字符串字面量 { 声明序列(可选) } |
(1) | ||||||||
extern 字符串字面量 声明 |
(2) | ||||||||
字符串字面量 | - | 一个未求值的字符串字面量,用于命名所需的语言连接 |
声明序列 | - | 一个声明序列,可以包括嵌套的连接规范 |
声明 | - | 一个声明 |
目录 |
[编辑] 解释
每个函数类型、每个具有外部连接的函数名称以及每个具有外部连接的变量名称都具有一个名为语言连接的属性。语言连接封装了与另一种编程语言编写的程序单元链接所需的要求集:调用约定、名称修饰(名称装饰)算法等。
保证仅支持两种语言连接
- "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 也声明一个函数。
与常规重新声明不同,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 程序共享,则 extern "C"(在 C 中不允许)必须使用适当的 #ifdef 隐藏,通常是 __cplusplus
#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++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
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]