为什么谷歌风格指南不鼓励前瞻性声明

Why does Google Style Guide discourage forward declaration?

本文关键字:前瞻性 声明 谷歌 风格 为什么      更新时间:2023-10-16

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

谷歌风格指南列出了远期申报的以下缺点

  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. 从一个标头向前声明多个符号可能比简单地#包含标头更详细。

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

然而,对SO的一些搜索似乎表明,前瞻性声明普遍是一个更好的解决方案。

那么,考虑到这些看似微不足道的缺点,有人能解释这种差异吗?

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

对SO的一些搜索似乎表明,远期申报普遍而言,这是一个更好的解决方案。

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

适当的包含文件仍将包含其自身的转发声明,对SO的搜索将表明这是正确的做法,所以我知道你从哪里得到SO支持声明的想法。但谷歌并不是说库自己的包含文件不应该包含正向声明,而是说你不应该耍无赖,为你想要使用的每个函数或类型编写自己的正向声明。

如果您#include正确的include文件,并且您的构建链工作,那么依赖关系不会被隐藏,并且的其余问题大多不适用,尽管include文件包含声明。仍然存在一些困难,但这不是谷歌在这里谈论的。

特别是将类型的正向声明与它们的类定义进行比较,(4)给出了一个出错的例子(因为D的正向声明不能表示它是从B派生的,因此需要类定义)。还有一种叫做"Pimpl"的技术,它确实使小心地将前向类型声明用于特定目的。因此,您将再次看到So对此的一些支持,但这与支持每个通常都应该围绕前向声明类而不是#include运行其头文件的想法不同。

来自Titus Winters的CppCon 2014演讲:

我们最近学到的一个重要问题是:向前声明任何带有模板的东西都是一个非常糟糕的主意。这导致了你不会相信的维护问题。转发声明是否可以,在某些情况下?我怀疑这条规则实际上会改为:鼓励库所有者提供一个标头,专门转发声明他们认为值得的东西(强调部分添加),你可能不应该转发声明自己,也不应该转发任何模板化类型。我们拭目以待。我们仍在根据所学内容找出[听不见的]细节。

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

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


编辑:

需要明确的是,我并不是说我同意或不同意谷歌对前瞻性声明的劝阻。我只是想了解他们的理由,并对<iosfwd>做了一个旁白/观察。

就我个人而言,我尽可能使用正向声明,遵循稍后在上面链接的GOTW中所述的指导方针(解决方案3):

指导原则:当一个正向声明就足够时,千万不要#include一个标头。

但温特斯的推理似乎也有其可取之处。我在代码中转发了第三方库中声明的模板化类型,语法确实很混乱(我还没有遇到Winters提到的维护问题)。OTOH,我不太确定是否会像谷歌C++风格指南中所说的那样阻止所有转发声明,但我想这对谷歌有效吗?

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