我的一个队友经常使用 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
将引发警告,因为它们显然无法访问 Foo.cpp
中定义的 ::Impl
。但是,我们不需要它们 - 这是一个仅用于 Foo.cpp
的类;我们不希望它在其他地方可见或为人所知。
虽然我们肯定会遇到 .cpp
文件包含多个 header 的情况,每个 header 都声明自己的 ::Impl
结构,但这些实际上并不冲突,因为这些结构从未在其各自的翻译单元之外使用。
tl;dr:这看起来很奇怪,会发出警告,看起来好像会导致冲突,但似乎确实有效。
综上所述,我不喜欢在我们的代码中嵌入如此深的引发警告的东西(它包含的头文件越多,删除它就越难。)它也只是一个 < em>ton 警告。
我的队友支持这一点,因为它很简单,保持代码定义简单,并让我们在所有代码中使用简短、一致的类名 Impl
。
我不是编码约定的坚持者;如果这对我们的用例来说是好的做法,我不介意。但我希望感到安心,因为它是安全且可维护的,并且不会在某些时候在我们面前爆炸。
最佳答案
Foo
类违反了 ODR。每个 cpp 文件都认为其唯一的 ptr 包含不同的类型。
ODR 违规会使您的程序格式错误,无需诊断。
你的程序可能会工作,但它的行为完全没有被 C++ 标准指定。
可能导致的实际问题是编译器可能会在您的脚下发生变化,并且当前未定义的行为(“它似乎可以工作”)将更改为其他东西(“强制转换意外失败”、“损坏的类型表”、“链接器无法链接”,“编译器证明类永远不能在其实现的翻译单元之外使用,并删除函数中的所有代码,就好像它们运行它会是 UB。”)作为示例,但没有限制如何它可能会变得疯狂。
有时做 UB 是有好处的,值得冒险。我在这里看到零 yield 。
创建一个 namespace FooImpl
或 Foo_details
并在其中填充 Impl
。
关于c++ - 使用匿名命名空间结构 : is this safe? 的 pimpl 习语,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39684438/