【问题标题】:Why does Google Style Guide discourage forward declaration?为什么 Google 样式指南不鼓励前向声明?
【发布时间】:2016-08-04 11:01:41
【问题描述】:

并不是说谷歌风格指南是圣经,但作为一个新手程序员,它似乎是一个很好的参考。

Google 样式指南列出了前向声明的以下缺点

  1. 前向声明可以隐藏依赖关系,允许用户代码在标头更改时跳过必要的重新编译。

  2. 随后对库的更改可能会破坏前向声明。函数和模板的前向声明可以防止标头所有者对其 API 进行其他兼容的更改,例如扩大参数类型、添加具有默认值的模板参数或迁移到新的命名空间。

    李>
  3. 从命名空间 std:: 转发声明符号会产生未定义的行为。

  4. 很难确定是否需要前向声明或完整的#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*)。

  1. 从标头前向声明多个符号可能比简单地#includeing 标头更冗长。

  2. 构造代码以启用前向声明(例如,使用指针成员而不是对象成员)会使代码变得更慢和更复杂。

但是,对 SO 的一些搜索似乎表明前向声明普遍是更好的解决方案。

那么鉴于这些看似不重要的缺点,有人可以解释这种差异吗?

什么时候可以安全地忽略部分或全部这些缺点?

【问题讨论】:

  • 企业风格指南是为最不熟练的企业代码猴子编写的。阅读它们时请记住这一点。第 4 点中给出的示例稍微扩展了这一点。带有 void* 类型参数的函数是一个损坏的函数。
  • “但是,对 SO 的一些搜索似乎表明前向声明普遍是更好的解决方案。” [需要引用]
  • 谷歌风格指南是为谷歌制作的。除非您是谷歌或类似公司,否则这些规则可能不适用于您。您必须记住,谷歌需要维护大约 1000 万行 C++ 代码,因此“不要使用异常”等一些规则是为了维护,因为重写 1000 万行代码以保证异常安全是不可行的。如果他们要从头开始,他们会做出不同的选择。
  • 前向声明会使分层分析(和模块)变得棘手。
  • 非 Google 观察者普遍对 AFAIR GSG 持怀疑态度,而不是被认为是“一个很好的参考”。它可能适用于谷歌,正如@nwp 所说,你需要在这种规模的项目中保持一致性......但它不应该被视为一般用户的参考。

标签: c++ forward-declaration google-style-guide


【解决方案1】:

对 SO 的一些搜索似乎表明前向声明是 普遍是更好的解决方案。

我不认为这是 SO 所说的。您引用的文本是将“游击队”前向声明与包含正确的包含文件进行比较。对于谷歌在这里批评的方法,我认为你不会在 SO 上找到很多支持。这种糟糕的做法是,“不,不要#include 包含文件,只需为您要使用的少数函数和类型编写声明”。

正确的包含文件仍将包含它自己的前向声明,搜索 SO 将表明这是正确的做法,所以我知道您从哪里得到 SO 支持声明的想法。但 Google 并不是说​​库自己的包含文件不应该包含前向声明,而是说您不应该为每个想要使用的函数或类型编写自己的前向声明。

如果您#include 正确的包含文件,并且您的构建链有效,则依赖项不会隐藏,并且其余问题大部分不适用,尽管事实上包含文件包含声明。仍然存在一些困难,但这不是 Google 在这里所说的。

特别查看类型的前向声明与它们的类定义相比,(4) 给出了一个出错的例子(因为 D 的前向声明不能表示它是从 B 派生的,因此你需要类定义)。还有一种称为“Pimpl”的技术确实小心将前向类型声明用于特定目的。因此,您将再次看到对 SO 的一些支持,但这与支持 everyone 通常应该围绕前向声明类而不是 #includeing 他们的标题的想法不同文件。

【讨论】:

  • 那么您如何评论我应该在 .hpp 文件中尽可能多地使用前向声明(最小化#include)并在 .cpp 中使用#include 的经验法则(或不?)文件?
  • @Woofas:我所说的一切都同样适用于 hpp 文件和 cpp 文件。有时在 hpp 文件中您最终会创建循环依赖关系,在这种情况下,最好通过重新组织文件的内容来解决这个问题,但作为绝对最后的手段,您可以接受前向声明的所有列出的缺点,并使用它们来工作围绕问题。
【解决方案2】:

来自 Titus Winters 的CppCon 2014 talk

我们最近学到的重要一点是:向前声明任何带有模板的东西是一个真的坏主意。这导致了您无法相信的维护问题。在某些情况下,前向声明可能没问题?我怀疑该规则实际上将更改为:鼓励图书馆所有者提供一个标头,专门转发声明他们认为值得的事情(强调添加),您可能不应该转发声明自己,没有人应该转发声明模板类型。走着瞧。我们仍在根据我们所了解的情况制定 [听不清] 细节。

因此,尝试直接转发声明模板类型的问题可能是他们阻止转发声明批发的动机之一...?

此外,提供“一个专门转发声明他们认为值得的东西的标头”听起来类似于<iosfwd> 的使用方式,如here(解决方案2.2)、here 和@987654324 所述@。


编辑:

明确地说,我并不是说我同意或不同意 Google 不鼓励提前声明。我只是想了解他们的基本原理,并对<iosfwd> 进行了旁白/观察。

就个人而言,我会尽可能使用前向声明,遵循上面链接的同一 GOTW 中稍后说明的指南(解决方案 3):

准则:当前向声明就足够时,切勿使用 #include 标头。

但温特斯的推理似乎也有一些优点。我处理过从第三方库转发声明的模板类型的代码,但语法确实变得混乱(我还没有遇到 Winters 提到的维护问题)。 OTOH,我不太确定是否要阻止 Google C++ 样式指南中所述的 所有 前向声明,但我想这对 Google 有用吗?

免责声明:我不是专家,还在学习中。

【讨论】:

  • 这个建议很有道理,C++ 标准库已经遵循它。有些标头为模板类型提供前向声明(例如iosfwd)。
  • 由于 cmets 在原始帖子中已关闭,我要指出该指南是错误的,或者至少与标准库规则(在 ISO/IEC 14882 中定义)不一致。这很容易导致不合格的代码,即使对于 cppreference 上的编辑器,直到 I reported it 并且它是 then corrected
猜你喜欢
  • 1970-01-01
  • 2012-10-03
  • 2018-03-04
  • 2016-09-14
  • 2015-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-05
相关资源
最近更新 更多