使用匿名命名空间结构的pimpl习语:这样安全吗?

时间:2016-09-25 07:28:59

标签: c++ namespaces pimpl-idiom incomplete-type

我的队友经常使用pimpl的变体,他喜欢这样:

foo.h中:

namespace { struct Impl; }

class Foo
{
public:
  Foo();
  ~Foo();

  void Bar(int n);
  /* ... */

private:
  std::unique_ptr<Impl> _impl;
};

这里发生的事情是他向前声明实现类在匿名命名空间中。然后他将在Foo.cpp中定义Impl类。

因此,::Impl翻译单元可以使用结构Foo.cpp的定义。包含Foo.h的其他代码会引发警告,因为他们显然无法访问::Impl中定义的Foo.cpp。但是,我们并不需要它们 - 它只是一个仅用于Foo.cpp的课程;我们不希望在其他地方看到或知道它。

虽然我们肯定会遇到.cpp文件包含多个标题的情况,每个标题都声明了自己的::Impl结构,但这些结构实际上并没有冲突,因为结构从未使用过在各自的翻译单位之外。

tl; dr:这看起来很奇怪,引发警告,看起来好像会引起冲突,但似乎确实有效。

所有这一切,我不满意在我们的代码中提出一些引发警告的东西(这个头文件越多,它就越难以取出)。 )它只是一个的警告。

我的队友支持这一点,因为它很简单,保持代码定义简单,并允许我们在所有代码中使用简短,一致的类名Impl

我不是编码惯例的坚持者;如果这是我们用例的好习惯,我不介意。但是我觉得这是安全和可维护的,并且在某些时候不会在我们的脸上爆炸。

1 个答案:

答案 0 :(得分:6)

班级Foo违反了ODR。每个cpp文件都认为它的唯一ptr包含不同的类型。

ODR违规会导致您的程序生成错误,无需诊断。

您的程序可能有效,但C ++标准完全没有指定它的行为。

可能导致的实际问题是编译器可能会在您的脚下发生变化,并且当前未定义的行为(&#34;它似乎工作&#34;)将更改为其他内容(&#34;强制转换失败&#34;# 34;,&#34;损坏类型表&#34;,&#34;链接器无法链接&#34;,&#34;编译器证明类永远不能在其实现的翻译单元之外使用,并删除所有代码你的功能就像他们运行它一样是UB。&#34;)作为例子,但它没有必然会有多疯狂。

做UB有时会带来好处,值得冒险。我认为这里没有任何好处。

创建namespace FooImplFoo_details并填充Impl