为什么 Google 风格指南不鼓励前向声明?
Why does Google Style Guide discourage forward declaration?
并不是说 Google 风格指南是圣经,但作为新手程序员,它似乎是一个很好的参考。
Google 风格指南列出了前向声明的以下缺点
前向声明可以隐藏依赖项,允许用户代码在 headers 更改时跳过必要的重新编译。
前向声明可能会被库的后续更改破坏。函数和模板的前向声明可以防止 header 所有者对其 API 进行 otherwise-compatible 更改,例如扩大参数类型、添加具有默认值的模板参数或迁移到新的命名空间。
转发声明来自命名空间 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*)
如果 B 和 D 的 #include 替换为前向声明,test() 将调用 f(void*)。
前向声明来自 header 的多个符号可能比简单地#includeing header.
更冗长
构建代码以启用前向声明(例如使用指针成员而不是 object 成员)会使代码更慢和更复杂。
然而,一些关于 SO 的搜索似乎表明前向声明是普遍更好的解决方案。
鉴于这些看似 non-trivial 的缺点,有人可以解释这种差异吗?
什么时候可以安全地忽略部分或全部这些缺点?
some search on SO seemed to suggest that forward declaration is
universally a better solution.
我不认为那是 SO 所说的。您引用的文本是将 "guerilla" 前向声明与包含正确的包含文件进行比较。对于 Google 在这里批评的方法,我认为您不会在 SO 上找到很多支持。那个糟糕的方法是,"no, don't #include
include files, just write declarations for the few functions and types you want to use".
正确的包含文件仍将包含它自己的前向声明,并且对 SO 的搜索将表明这是正确的做法,所以我知道您从哪里得到 SO 支持声明的想法。但是 Google 并不是说库自己的包含文件不应该包含前向声明,而是说你不应该无赖地为你想使用的每个函数或类型编写你自己的前向声明。
如果您 #include
正确的包含文件,并且您的构建链有效,那么依赖关系不会被隐藏,其余的问题 大多数 不会适用,尽管包含文件包含声明。还有一些困难,但这不是Google这里所说的。
与类型的 class 定义相比,特别关注类型的前向声明,(4) 给出了一个出错的例子(因为 D
的前向声明不能表达它是派生自 B
,为此你需要 class 定义)。还有一种称为 "Pimpl" 的技术确实可以 小心 为特定目的使用前向类型声明。所以你会再次看到一些关于 SO 的支持,但这与支持 每个人 通常 运行 围绕前向声明 classes 而不是 #include
ing 他们的头文件。
来自泰特斯·温特斯的 CppCon 2014 talk:
The big one that we've learned lately is: Forward declaring anything with a template in it is a really bad idea. This led to maintenance problems like you would not believe. Forward declaration may be okay, in some cases? My suspicion is the rule is actually going to change to: Library owners are encouraged to provide a header that specifically forward declares the things that they think are worth it (emphasis added), and you probably shouldn't forward declare yourself, and nobody should ever forward declare a templated type. We'll see. We're still working out the [inaudible] details from what we've learned.
因此,尝试直接转发声明模板类型的问题可能是他们阻止转发声明批发的动机之一......?
此外,提供 "a header that specifically forward declares the things that they think are worth it" 听起来类似于 <iosfwd>
的使用方式,如 here (Solution 2.2), here, and here 所述。
编辑:
需要说明的是,我并不是说我同意或不同意 Google 对提前声明的劝阻。我只是想了解他们的理由,然后 aside/observation 关于 <iosfwd>
.
就个人而言,我会尽可能使用前向声明,遵循上面相同 GOTW 中后面所述的指南(解决方案 3):
Guideline: Never #include
a header when a forward declaration will suffice.
但温特斯的推理似乎也有一些道理。我处理过从第三方库转发声明的模板化类型的代码,语法确实变得混乱(我还没有 运行 进入 Winters 提到的维护问题)。 OTOH,我不太确定 all 前向声明是否如 Google C++ 风格指南中所述,但我想这对 Google 有用吗?
免责声明:我不是专家,仍在学习中。
并不是说 Google 风格指南是圣经,但作为新手程序员,它似乎是一个很好的参考。
Google 风格指南列出了前向声明的以下缺点
前向声明可以隐藏依赖项,允许用户代码在 headers 更改时跳过必要的重新编译。
前向声明可能会被库的后续更改破坏。函数和模板的前向声明可以防止 header 所有者对其 API 进行 otherwise-compatible 更改,例如扩大参数类型、添加具有默认值的模板参数或迁移到新的命名空间。
转发声明来自命名空间 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*)
如果 B 和 D 的 #include 替换为前向声明,test() 将调用 f(void*)。
前向声明来自 header 的多个符号可能比简单地#includeing header.
更冗长
构建代码以启用前向声明(例如使用指针成员而不是 object 成员)会使代码更慢和更复杂。
然而,一些关于 SO 的搜索似乎表明前向声明是普遍更好的解决方案。
鉴于这些看似 non-trivial 的缺点,有人可以解释这种差异吗?
什么时候可以安全地忽略部分或全部这些缺点?
some search on SO seemed to suggest that forward declaration is universally a better solution.
我不认为那是 SO 所说的。您引用的文本是将 "guerilla" 前向声明与包含正确的包含文件进行比较。对于 Google 在这里批评的方法,我认为您不会在 SO 上找到很多支持。那个糟糕的方法是,"no, don't #include
include files, just write declarations for the few functions and types you want to use".
正确的包含文件仍将包含它自己的前向声明,并且对 SO 的搜索将表明这是正确的做法,所以我知道您从哪里得到 SO 支持声明的想法。但是 Google 并不是说库自己的包含文件不应该包含前向声明,而是说你不应该无赖地为你想使用的每个函数或类型编写你自己的前向声明。
如果您 #include
正确的包含文件,并且您的构建链有效,那么依赖关系不会被隐藏,其余的问题 大多数 不会适用,尽管包含文件包含声明。还有一些困难,但这不是Google这里所说的。
与类型的 class 定义相比,特别关注类型的前向声明,(4) 给出了一个出错的例子(因为 D
的前向声明不能表达它是派生自 B
,为此你需要 class 定义)。还有一种称为 "Pimpl" 的技术确实可以 小心 为特定目的使用前向类型声明。所以你会再次看到一些关于 SO 的支持,但这与支持 每个人 通常 运行 围绕前向声明 classes 而不是 #include
ing 他们的头文件。
来自泰特斯·温特斯的 CppCon 2014 talk:
The big one that we've learned lately is: Forward declaring anything with a template in it is a really bad idea. This led to maintenance problems like you would not believe. Forward declaration may be okay, in some cases? My suspicion is the rule is actually going to change to: Library owners are encouraged to provide a header that specifically forward declares the things that they think are worth it (emphasis added), and you probably shouldn't forward declare yourself, and nobody should ever forward declare a templated type. We'll see. We're still working out the [inaudible] details from what we've learned.
因此,尝试直接转发声明模板类型的问题可能是他们阻止转发声明批发的动机之一......?
此外,提供 "a header that specifically forward declares the things that they think are worth it" 听起来类似于 <iosfwd>
的使用方式,如 here (Solution 2.2), here, and here 所述。
编辑:
需要说明的是,我并不是说我同意或不同意 Google 对提前声明的劝阻。我只是想了解他们的理由,然后 aside/observation 关于 <iosfwd>
.
就个人而言,我会尽可能使用前向声明,遵循上面相同 GOTW 中后面所述的指南(解决方案 3):
Guideline: Never
#include
a header when a forward declaration will suffice.
但温特斯的推理似乎也有一些道理。我处理过从第三方库转发声明的模板化类型的代码,语法确实变得混乱(我还没有 运行 进入 Winters 提到的维护问题)。 OTOH,我不太确定 all 前向声明是否如 Google C++ 风格指南中所述,但我想这对 Google 有用吗?
免责声明:我不是专家,仍在学习中。