在头文件中使用 using namespace 而不导出符号
问题
在头文件中定义对外导出的常量时,使用 std::literals::string_view_literals 中的 operator""sv() 能简化 string_view 常量的定义。例如:
constexpr auto MY_STR = "text"sv; // 清晰简洁但直接在头文件中直接使用 using namespace 存在严重风险:
- 可能污染用户的全局命名空间,导致符号冲突
- 作为库使用时,会不可控地影响用户代码
- 破坏命名空间封装性,违背良好设计原则
解决方案 - 使用双层嵌套的 namespace 隔离命名空间
- 在外层
namespace project::detail导入所需的其他命名空间的符号 - 在内层
namespace project::detail::impl中定义需要导出的符号 - 在导出的命名空间
namespace project中,仅通过 using namespace 导出内层中你希望暴露的子命名空间impl,而不导出引入的外部命名空间内容
例:
- 项目命名空间:
namespace project - 不导出的命名空间:
namespace project::D - 导出的命名空间:
namespace project::D::I
则可以通过如下方式定义 namespace project 中可用的符号而不导出 namespace project::D 中引入的符号:
// ------------ test.hpp ------------
#include <string_view>
// 外层不导出的 命名空间 D
namespace project::D {
using namespace std::literals::string_view_literals;
// 需要导出符号的 命名空间 I
namespace I {
// 需要导出的符号
constexpr auto FOO = "bar"sv;
constexpr inline auto get_baz() noexcept { return "baz"sv; }
} // namespace I
} // namespace project::D
namespace project {
// 向 namespace project 中仅导入 I 命名空间中的符号
using namespace D::I;
} // namespace project
// ------------ main.cpp ------------
int main() {
// 可以直接使用 project::D::I 中的符号
auto var = project::FOO;
auto var2 = project::get_baz();
}注意
嵌套的 namespace 要尽可能地简短
编译器在打印错误信息的时候会使用原始的命名空间。如果名称过长会导致编译错误信息过长,难以阅读。
版权所有
版权归属:nmpassthf
