3

我正在与图书馆合作。该库有一个文件,其中包含我实际希望向其他程序公开的接口,包含在foo.hfoo.cpp. 它还包含一堆帮助类和实用函数,位于文件、、、、bar1.hbar2.h中。bar1.cppbar2.cpp

如果我编译所有这些文件并将它们粘贴到 .lib 中,我遇到的问题是bar文件中的某些符号具有非常常见的名称,与我需要链接的其他外部库中的名称发生冲突。

如果所有代码都在一个 .cpp 文件中,我知道如何解决这个问题:我可以使用staticnamespace { }阻止链接器导出内部符号。但显然,bar extern如果我想在foo.

我可以将所有 .cpp 文件包装在namespace baz { }. 如果我仔细选择 baz,那么它与其他库中使用的命名空间冲突的可能性很小,那将大大解决问题。但理想情况下,除了in 符号之外的任何内容都不foo.h应导出到我的 .lib 中。有这样做的技术吗?

4

1 回答 1

3

可以实现这一点,但需要付出一些代价:

在 C++ 中,您可以有内部链接。未命名命名空间内的任何内容都具有内部链接*(见脚注),以及静态自由函数(您应该更喜欢匿名命名空间)。

更新:这是来自§3.5,4的 C++11 标准引用:

未命名的命名空间或在未命名的命名空间中直接或间接声明的命名空间具有内部链接。所有其他命名空间都有外部链接。具有命名空间范围但没有在上面给出内部链接的名称,如果它是
一个变量的名称,则它与封闭命名空间具有相同的链接;或者
——一个函数;或
— 命名类(第 9 条),或在 typedef 声明中定义的未命名类,其中该类具有用于链接目的的 typedef 名称(7.1.3);或
— 命名枚举 (7.2),或在 typedef 声明中定义的未命名枚举,其中枚举具有用于链接目的的 typedef 名称 (7.1.3);或
- 属于具有链接的枚举的枚举数;或者
——一个模板。

但是,内部链接适用于翻译单元,而不适用于静态库。因此,如果您使用通常的方法将每个类放在其自己的翻译单元 (=cpp) 中,则无法在匿名命名空间中定义它们,因为您无法将它们链接在一起以构建库。
您可以通过使整个库成为一个单一的翻译单元来解决这个难题:一个提供库的公共接口的标头,一个带有函数定义的源,以及在匿名命名空间中定义的任何其他标头:

mylib.hpp

class MyLib {
public:
 int foo();
 double bar(int i);
};

mylib.cpp

#include "mylib.hpp"
#include "mylibimpl.h"

int MyLib::foo() {
  return fooimpl();
}

double MyLib::bar(int i) {
  return BarImpl(i).do();
}

mylibimpl.h

namespace {
  inline int fooimpl() { return 42; }

  class BarImpl {
    double d;
  public:

    BarImpl(int i) : d(i*3.42) {}
    double do() { return 2*d; }
  };
}

您现在将拥有一个翻译单元 ( mylib.o/ mylib.lib),并且所有的*impl类和函数都无法从外部看到,因为它们具有内部链接。

代价是您必须重新组织内部类的源代码(例如解决循环依赖),并且库内部代码的每一次简单更改都会导致库中所有内容的大重新编译,因为只有一个巨大的翻译单元。因此,只有在库代码本身非常稳定或库不太大的情况下,才应该这样做。除了完全隐藏内部符号之外的好处是编译器将能够提取它想要的任何优化,因为没有实现细节隐藏在不同的翻译单元中。

*脚注: 正如 Billy ONeal 所评论的,在 C++03 中,匿名命名空间中的实体不一定具有内部链接。但是,如果它们具有外部链接,则它们具有对其翻译单元唯一的名称,并且实际上无法从该 TU 外部访问,这意味着该过程也适用于 C++03。

于 2013-06-04T18:38:01.993 回答