首页 > 解决方案 > 让细节命名空间代码看不见 - 优雅

问题描述

假设我正在编写一个仅包含标头或主要包含标头的库,并且具有以下代码:

using my_type = int;

namespace detail {
    inline void foo() { my_type x; do_foo_stuff(x); }
}

inline void bar() { do_bar_stuff(); detail::foo(); }
inline void baz(my_type y) { do_baz_stuff(y); detail::foo(); }

我想放在foo()不同的文件中。动机是我有很多这样的详细和不详细的函数,我希望带有我的公共 API 的标头不会被 中出现的内容弄乱detail,并且不打算直接使用。

问题是 - 这样做的惯用方法是什么?

所以它不能是这两个选项之一。

标签: c++idiomsforward-declaration

解决方案


因为通常头文件包含声明,而源文件包含实现,所以人们认为头文件是接口、公共 api,而源文件包含实现细节。在库中,用户(库的消费者)看不到源文件的内容,但可以看到标题,这一事实进一步强化了这一点。

然而这是错误的:

  • 标头确实包含定义(模板、内联函数和变量),
  • 标头包含私有类成员的声明和具有内部链接的符号(静态非本地和匿名命名空间)。这显然不是 API 的一部分。
  • 一个库将公开一个符号(类、函数),即使它仅在内部使用并且不是 API 的一部分(除非它仅在一个 CU 上使用并通过内部链接制作)
  • 库标头会将其使用的所有标头引入消费者代码,即使库 API 不需要其中一些标头。这将在用户代码中带来不需要的符号,使命名空间变得混乱。

头文件和源文件的分离不是在公共接口/实现屏障上完成的。这种代码分离只是 C++ 及其 C 遗产的设计方式的产物。C++ 不需要多通道编译器,因此它需要在使用前声明和一个定义。所以标题是一个解决方案。它们不是 API 规范。

所以不幸的结论是 C++ 没有 API / 实现分离,尝试使用标头是不成功的。

现在回答您的问题:我所知道的惯用方式确实是使用detailsorimpl命名空间。据了解,以这种方式命名的命名空间包含库实现细节,不应在用户代码中使用。我个人不会改变你最初的设计。


C++20 终于引入了模块,afaik 解决了这个问题。现在,我们将在消费者和公共 API 中看不到的内部符号明确区分开来。


推荐阅读