我的队友经常使用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
。
我不是编码惯例的坚持者;如果这是我们用例的好习惯,我不介意。但是我觉得这是安全和可维护的,并且在某些时候不会在我们的脸上爆炸。
答案 0 :(得分:6)
班级Foo
违反了ODR。每个cpp文件都认为它的唯一ptr包含不同的类型。
ODR违规会导致您的程序生成错误,无需诊断。
您的程序可能有效,但C ++标准完全没有指定它的行为。
可能导致的实际问题是编译器可能会在您的脚下发生变化,并且当前未定义的行为(&#34;它似乎工作&#34;)将更改为其他内容(&#34;强制转换失败&#34;# 34;,&#34;损坏类型表&#34;,&#34;链接器无法链接&#34;,&#34;编译器证明类永远不能在其实现的翻译单元之外使用,并删除所有代码你的功能就像他们运行它一样是UB。&#34;)作为例子,但它没有必然会有多疯狂。
做UB有时会带来好处,值得冒险。我认为这里没有任何好处。
创建namespace FooImpl
或Foo_details
并填充Impl
。