语言链接
提供以不同程序语言编写的模块间的链接。
extern 字符串字面量 { 声明序列(可选) }
|
(1) | ||||||||
extern 字符串字面量 声明
|
(2) | ||||||||
字符串字面量 | - | 所要求的语言链接的名字 |
声明序列 | - | 声明的序列,可以包含嵌套的链接说明 |
声明 | - | 一个声明 |
解释
所有函数类型,所有拥有外部链接的函数名,及所有拥有外部链接的变量名,拥有一种称作语言链接的性质。语言链接封装与以另一程序语言编写的模块进行链接的要求的集合:调用约定、名字重整的算法,等等。
仅保证支持二种语言链接:
- "C++",默认的语言链接。
- "C",使得以 C 程序语言编写的函数进行链接,以及在 C++ 程序中定义能从 C 模块调用的函数成为可能。
extern "C" { int open(const char *pathname, 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 并接受指向返回 void 且不接受形参的 C 函数的指针 extern "C" typedef void FUNC(); // 声明 FUNC 为返回 void 且不接受形参的 C 函数类型 FUNC f2; // 名字 f2 拥有 C++ 链接,但其类型是 C 函数 extern "C" FUNC f3; // 名字 f3 拥有 C 链接且其类型是 C 函数 void(void) void (*pf2)(FUNC*); // 名字 pf2 拥有 C++ 链接,且其类型是“指向返回 void 并接受 // 一个‘指向返回 void 且不接受形参的 C 函数的指针’类型参数的 C++ 函数的指针 extern "C" { static void f4(); // 函数 f4 的名字拥有内部链接(无语言) // 但函数的类型拥有 C 语言链接 }
在同一命名空间中具有相同名字和相同形参列表的两个函数,不能拥有两个不同的语言链接(然而需要注意,形参的链接可容许这种重载,例如 std::qsort 和 std::bsearch 的情况)。类似地,同一命名空间中的两个变量不能拥有两种不同的语言链接。
"C" 链接的特殊规则
- 当类成员的声明和成员函数类型的声明出现于 "C" 语言块中时,其链接仍保持为 "C++"。
- 当两个拥有同一无限定名字的函数声明于不同命名空间,且都拥有 "C" 语言链接时,两个声明表示同一函数。
- 当两个拥有 "C" 语言链接和相同名字的变量出现于不同命名空间时,它们表示同一变量。
- 一个 "C" 变量和一个 "C" 函数不能拥有相同的名字,无关乎它们定义于同一或不同命名空间。
注解
语言链接只能出现在命名空间作用域。
语言说明的花括号不建立作用域。
当语言说明发生嵌套时,有效的是最内层的说明。
函数可在带语言说明的声明之后,进行不带链接说明的重声明,第二个声明将重复使用首个声明的语言链接。反之则不行:若首个声明无语言链接,则假定其为 "C++",而以其他语言链接重声明则是错误。
直接包含于语言链接说明之中的声明,被处理为如同它含有 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++ 程序中包含(include)含有 C 库函数的声明的头文件成为可能,但如果与 C 程序共用相同的头文件,必须以适当的
#ifdef 隐藏 extern "C"
(其在 C 中不被允许),通常为 __cplusplus:
#ifdef __cplusplus extern "C" int foo(int, int); // C++ 编译器所见 #else int foo(int, int); // C 编译器所见 #endif
仅有的区别带 "C" 与 "C++" 语言链接的函数类型的现代编译器是 Oracle Studio ,其他编译期不容许仅在语言链接有别的重载,包括 C++ 标准要求的重载集( std::qsort、 std::bsearch、 std::signal、 std::atexit 及 std::at_quick_exit ): GCC bug 2316 、 Clang bug 6277 、 CWG1555 。
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 and C++ language linkages shall not differentiate function types."); // 多数编译器中下列声明不声明重载 // 因为 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++20 standard (ISO/IEC 14882:2020):
- 9.11 Linkage specifications [dcl.link]
- C++17 standard (ISO/IEC 14882:2017):
- 10.5 Linkage specifications [dcl.link]
- C++14 standard (ISO/IEC 14882:2014):
- 7.5 Linkage specifications [dcl.link]
- C++11 standard (ISO/IEC 14882:2011):
- 7.5 Linkage specifications [dcl.link]
- C++03 standard (ISO/IEC 14882:2003):
- 7.5 Linkage specifications [dcl.link]