21

并不是说 Google Style Guide 是圣经,但作为一个新手程序员,它似乎是一个很好的参考。

Google Style Guide 列出了前向声明的以下缺点

  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

2 回答 2

10

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

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

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

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

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

于 2016-04-13T11:39:59.980 回答
7

来自 Titus Winters 的CppCon 2014 演讲

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

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

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


编辑:

需要明确的是,我并不是说我同意或不同意 Google 不鼓励提前声明。我只是想了解他们的理由,并对<iosfwd>.

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

准则:#include当前向声明就足够时,永远不要使用标题。

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

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

于 2016-11-26T08:24:59.813 回答