不是说Google风格指南是神圣的圣经,但作为一名新手程序员,它似乎是一个很好的参考。
Google样式指南列出了前向声明的以下缺点
前向声明可以隐藏依赖项,允许用户代码在标题更改时跳过必要的重新编译。
对库的后续更改可能会破坏前向声明。函数和模板的前向声明可以防止标题所有者对其API进行其他兼容的更改,例如扩展参数类型,添加具有默认值的模板参数或迁移到新的命名空间。
从名称空间std ::转发声明符号会产生未定义的行为。
可能很难确定是否需要前向声明或完整#include。用前向声明替换#include可以默默地改变代码的含义:
代码:
// b.h:
struct B {};
struct D : B {};
// good_user.cc:
#include "b.h"
void f(B*);
void f(void*);
void test(D* x) { f(x); } // calls f(B*)
如果#include被替换为B和D的前向decls,test()将调用f(void *)。
从标题中声明多个符号可能比简单地#include the header更加冗长。
构造代码以启用前向声明(例如,使用指针成员而不是对象成员)可以使代码更慢,更复杂。
然而,对SO的一些搜索似乎表明,前向声明通常是更好的解决方案。
因此,考虑到这些看似非平凡的缺点,有人可以解释这种差异吗?
何时可以安全地忽略部分或全部这些缺点?
答案 0 :(得分:6)
对SO的一些搜索似乎表明前向声明是 普遍是一个更好的解决方案。
我认为这不是SO所说的。你引用的文字是比较“游击队”前方声明,包括正确的包含文件。对于谷歌在这里批评的方法,我认为你不会在SO上找到很多支持。这种糟糕的方法是,“不,不要#include
包含文件,只需为你想要使用的少数函数和类型编写声明”。
正确的包含文件仍将包含自己的前向声明,并且对SO的搜索将表明这是正确的事情,所以我看到你在哪里认为SO赞成声明。但谷歌并不是说图书馆自己的包含文件不应该包含前向声明,而是说你不应该流氓并为你想要使用的每个函数或类型编写自己的前向声明。
如果您#include
使用正确的包含文件,并且您的构建链正常工作,那么依赖关系不会被隐藏,其余的问题主要不适用,尽管事实如此包含文件包含声明。仍有一些困难,但这不是谷歌在这里所说的。
特别关注类型的前向声明与它们的类定义相比较,(4)给出了一个错误的例子(因为D
的前向声明不能表明它是从{{1}派生的为此,你需要类定义)。还有一种称为“Pimpl”的技术,它确实为特定目的使用正向类型声明小心。所以你再次看到SO的一些支持,但这与支持每个人应该通常围绕前向声明的类而不是B
的想法不同他们的头文件。
答案 1 :(得分:4)
来自Titus Winters'CppCon 2014 talk:
我们最近学到的最重要的一点是:向前声明任何带有模板的内容是真的坏主意。这导致了您不相信的维护问题。在某些案例中,转发声明可能没问题吗?我的怀疑是规则实际上将改为:鼓励图书馆所有者提供一个标题,专门向前宣布他们认为值得的东西(强调添加),你可能不应该转发声明自己,没有人应该转发声明模板类型。走着瞧。我们仍然在研究我们所学到的[听不清]细节。
因此,尝试直接转发声明模板类型的问题可能是他们阻止前向声明批发的动机之一......?
此外,提供“专门向前声明他们认为值得的东西的标题”听起来与使用<iosfwd>
的方式类似,如here所述(解决方案2.2),{{3 }和here。
修改强>
要明确的是,我并不是说我同意或不同意谷歌对前瞻性声明的劝阻。我只是想了解他们的理由,并对<iosfwd>
进行了一次/观察。
就个人而言,我会尽可能地使用前瞻性声明,遵循后面在上面链接的同一here中提出的指南(解决方案3):
指南:当前瞻声明足够时,永远不要
#include
标题。
但温特斯的推理似乎也有一些优点。我已经处理了代码,我从第三方库转发声明的模板化类型,语法确实变得混乱(我还没有遇到Winters提到的维护问题)。 OTOH,我不太确定如Google C ++风格指南中所述的那样阻止所有前向声明,但我想这对Google有用吗?
免责声明:我不是专家,还在学习。