Ziv*_*Ziv 2 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;
};
Run Code Online (Sandbox Code Playgroud)
这里发生的事情是他正在声明实现类在匿名命名空间中.然后他将Impl在Foo.cpp中定义类.
因此,结构的定义::Impl将可供Foo.cpp翻译单位使用.其他代码包含Foo.h会引发警告,因为它们显然无法访问::Impl定义的内容Foo.cpp.但是,我们不需要它们 - 它只是一个Foo.cpp只用于它的类; 我们不希望它在其他地方可见或已知.
虽然我们当然可以在.cpp文件中包含多个标题,每个标题都声明自己的::Impl结构,但实际上并没有发生冲突,因为结构永远不会在各自的翻译单元之外使用.
tl; dr:这看起来很奇怪,引发警告,看起来好像会引起冲突,但似乎确实有效.
所有这一切,我不满意我的代码中提出了一些警告,这些警告已经深入到我们的代码中(这个文件越多,它就越难以取出.)这也只是一大堆警告.
我的队友坚持这一点,因为它很简单,保持代码定义简单,并允许我们Impl在所有代码中使用简短,一致的类名.
我不是编码惯例的坚持者; 如果这是我们用例的好习惯,我不介意.但是我觉得这是安全和可维护的,并且在某些时候不会在我们的脸上爆炸.
该类Foo违反了ODR.每个cpp文件都认为其唯一的ptr包含不同的类型.
ODR违规会使您的程序生成错误,无需诊断.
您的程序可能有效,但C++标准完全没有指定它的行为.
可能导致的实际问题是编译器可能会在您的脚下发生变化,并且当前未定义的行为("它似乎工作")将更改为其他内容("强制转换失败","损坏类型表","链接器未能链接","编译器证明类是永远不能在其实现的转换单元之外使用,并且擦除函数中的所有代码,就好像它们运行它将是UB.")作为示例,但是没有必要如此疯狂它可以得到.
做UB有时会带来好处,值得冒险.我看到这里没有任何好处.
在那里创建一个namespace FooImpl或者Foo_details填充内容Impl.