考虑以下:
namespace N {
extern "C" void f();
}
void g() {
N::f();
}
此代码在命名空间内声明了一个带有 C 链接的外部函数。这使得从私有命名空间引用此类函数成为可能,避免了由普通全局外部声明引起的命名空间污染。它还允许客户端代码为同一函数发出其他(希望是兼容的)声明而不会发生冲突,即使在全局命名空间中,也可能源自供应商提供的标头包含。
我经常依赖 C 和 C++ 中的类似结构来将编译与某些库提供的写得不好或冲突的头文件隔离开来。(在 C 中,这是通过在函数范围内发出所需的声明来实现的,如果不是extern
在函数范围内不允许链接声明,这在 C++ 中也是可能的。)这对于正确链接到明确定义的ABI 无需依赖供应商提供的头文件。
是否可以对具有常规 C++ 链接的函数或方法执行相同的操作?也就是说:在私有命名空间内(或在任何类型的本地范围内)声明一个具有 C++ 链接的外部函数,但它可能指的是实际定义在另一个命名空间内的函数?
预期功能(伪代码):
namespace N {
// Actually should link with P::f() (and not N::f()).
extern "C++" void f();
}
void g() {
N::f(); // P::f();
}
这对于源文件(与头文件相反)显然不是问题,因为在这种情况下命名空间污染无关紧要。因此,这个问题主要是指隔离库头文件中的声明(用于模板和内联函数)。
欢迎使用特定于编译器的解决方案(对 MSVC 和 GCC 感兴趣)。
示例:假设我的库被调用Lib1
,并且我想声明Lib1
命名空间内的所有内容。
// Lib1.hpp
namespace Lib1 {
class Class1;
void func1();
// ...
}
现在假设我的库引用了另一个库,Lib2
是别人提供的 C 库。
/* Lib2.h */
#ifdef __cplusplus
extern "C" {
#endif
struct Struct2;
void func2();
/* ... */
#ifdef __cplusplus
}
#endif
在我的库中,如果出于某种原因需要,我可以引用实体Lib2
而无需包含:Lib2.h
// Lib1.hpp
namespace Lib1 {
extern "C" void func2();
inline void inlineX() {
func2();
}
}
同时,客户端代码可以自由地包含两者Lib1.hpp
并且Lib2.h
(考虑到它是 C++ 友好的)而不会发生冲突。
现在,假设有第三个库 ,Lib3
它是一个 C++ 库并在Lib3
命名空间中声明实体。
// Lib3.hpp
namespace Lib3 {
class Class3;
void func3();
// ...
}
有没有办法以与Lib3
相同的方式关联Lib2
?即:引用Lib3
inside中的实体Lib1.hpp
,不包括Lib3.hpp
但仍然允许客户端代码同时包括这两者Lib1.hpp
并且Lib3.hpp
没有麻烦?
如果,在 中Lib1
,它被声明为:
// Lib1.hpp
namespace Lib3 {
void func3();
}
namespace Lib1 {
inline void inlineY() {
Lib3::func3();
}
}
那么,如果客户端代码同时包含两者Lib1.hpp
,则可能会发生冲突Lib3.hpp
——当然不是在这个声明相同的简单示例中,但实际情况中的细微差异可能会在语法级别触发警告或错误,即使底层 ABI 相同,因为这违反了不在Lib1
命名空间之外声明任何内容的前提。
希望这有助于理解这个问题。