Why does Google Style Guide discourage forward declaration?
Asked Answered
N

2

23

Not to say that the Google Style Guide is the holy bible but as a newbie programmer, it seems like a good reference.

The Google Style Guide lists the following disadvantages of forward declaration

  1. Forward declarations can hide a dependency, allowing user code to skip necessary recompilation when headers change.

  2. A forward declaration may be broken by subsequent changes to the library. Forward declarations of functions and templates can prevent the header owners from making otherwise-compatible changes to their APIs, such as widening a parameter type, adding a template parameter with a default value, or migrating to a new namespace.

  3. Forward declaring symbols from namespace std:: yields undefined behavior.

  4. It can be difficult to determine whether a forward declaration or a full #include is needed. Replacing an #include with a forward declaration can silently change the meaning of code:

Code:

  // 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*)

If the #include was replaced with forward decls for B and D, test() would call f(void*).

  1. Forward declaring multiple symbols from a header can be more verbose than simply #includeing the header.

  2. Structuring code to enable forward declarations (e.g. using pointer members instead of object members) can make the code slower and more complex.

However, some search on SO seemed to suggest that forward declaration is universally a better solution.

So given these seemingly non-trivial disadvantages, can someone explain this discrepancy?

And when is it safe to ignore some or all of these disadvantages?

Nydia answered 13/4, 2016 at 11:24 Comment(7)
corporate style guides are written with the least skilled corporate code monkey in mind. bear that in mind when reading them. The example given in point 4 is stretching the point a bit. A function with an argument of type void* is a broken function.Mehalek
"However, some search on SO seemed to suggest that forward declaration is universally a better solution." [citation needed]Lax
The google style guide is made for google. Unless you are google or a comparable company the rules might not apply to you. You have to keep in mind that google needs to maintain ~10 million lines of C++ code, so some rules such as "Don't use exceptions" are for maintenance because rewriting 10 million lines of code to be exception safe is not feasible. If they were to start from scratch they would make different choices.Jamikajamil
Forward declarations can make layering analysis (and modules) tricky.Merciless
Rather than being considered "a good reference", AFAIR the GSG is generally viewed sceptically by non-Google observers. It presumably works for Google, and as @Jamikajamil says, you need consistency in a project of that scale... but it shouldn't be seen as a reference for general users.Sachs
my company's style guide recommends the use of forward declaration because they said that they have done tests and forward declaration improveds compilation time on very big products a great dealCinerator
Forward declarations should be in the .H file not in the .CPP. So point 4 is wrong. Forward declarations minimise the dragging in in the .H of other .H's where the class definitions are. Of course if you inherit from another class or have member objects, you have no choice, the full definition is needed, a forward declaration will not suffice.Shroud
C
10

some search on SO seemed to suggest that forward declaration is universally a better solution.

I don't think that's what SO says. The text you quote is comparing a "guerilla" forward declaration against including the proper include file. I don't think you'll find a lot of support on SO for the approach Google is criticising here. That bad approach is, "no, don't #include include files, just write declarations for the few functions and types you want to use".

The proper include file will still contain forward declarations of its own, and a search on SO will suggest that this is the right thing to do, so I see where you got the idea that SO is in favour of declarations. But Google isn't saying that a library's own include file shouldn't contain forward declarations, it's saying that you shouldn't go rogue and write your own forward declaration for each function or type you want to use.

If you #include the right include file, and your build chain works, then the dependency isn't hidden and the rest of the problems mostly don't apply, despite the fact that the include file contains declarations. There are still some difficulties, but that's not what Google is talking about here.

Looking in particular at forward declarations of types as compared with class definitions for them, (4) gives an example of that going wrong (since a forward declaration of D cannot express that it's derived from B, for that you need the class definition). There's also a technique called "Pimpl" that does make careful use of a forward type declaration for a particular purpose. So again you'll see some support on SO for that, but this isn't the same as supporting the idea that everyone should in general run around forward-declaring classes instead of #includeing their header files.

Conti answered 13/4, 2016 at 11:39 Comment(2)
So how would you comment on the rule of thumb (or not?) that I should use forward declaration as much as possible (minimize #include) in .hpp files and use #include in the .cpp files?Nydia
@Woofas: Everything that I've said applies equally in hpp files and cpp files. Occasionally in hpp files you end up creating circular dependencies, in which case it's best to fix that by reorganising the contents of the files, but as an absolute last resort you could accept all of the listed disadvantages of forward declarations, and use them to work around the problem.Conti
A
7

From Titus Winters' 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.

So maybe issues with trying to directly forward declare templated types might be one of their motives for discouraging forward declaration wholesale...?

Also, providing "a header that specifically forward declares the things that they think are worth it" sounds similar to the way <iosfwd> is used, as described here (Solution 2.2), here, and here.


EDIT:

To be clear, I wasn't saying I agree or disagree with Google's discouragement of forward declaration. I was just trying to understand their rationale, and made an aside/observation about <iosfwd>.

Personally, I use forward declarations whenever I can, following the guideline stated later in that same GOTW linked above (Solution 3):

Guideline: Never #include a header when a forward declaration will suffice.

but Winters' reasoning seems to have some merit as well. I've worked on code where I forward declared templated types from a third party library, and the syntax does get messy (I haven't yet run into the maintenance problems Winters alluded to). OTOH, I'm not so sure about discouraging all forward declarations as stated in the Google C++ Style Guide, but I guess that's what works for Google?

Disclaimer: I'm no expert, still learning.

Arv answered 26/11, 2016 at 8:24 Comment(2)
This advice makes so much sense, it is already followed by the C++ standard library. There are headers that provide forward declarations for templated types (e.g., iosfwd).Shopworn
As the comments are closed in the original post, I'd point out the guideline is wrong, or at least inconsistent to the standard library rules (defined in ISO/IEC 14882). This would easily cause nonconforming code, even for editors on cppreference, until I reported it and it was then corrected.Rorie

© 2022 - 2024 — McMap. All rights reserved.