9

For every pointer p1 of class class1, are there any risks we should consider by using a forward declaration of class1 instead of including class1 header file?

I can see only the advantage: The header file will have less size.

Stav Alfi
  • 13,139
  • 23
  • 99
  • 171
  • 5
    The compiler will tell you. – juanchopanza Dec 13 '17 at 20:27
  • Do you... need to know *anything* else about `class1` besides the fact that it exists? Its size, its members, ...? – Barry Dec 13 '17 at 20:28
  • 2
    You can't do that much with a forward declaration. – Galik Dec 13 '17 at 20:28
  • @juanchopanza I'm sure you will get some votes for this comment. Now can you provide a good answer from your experience? – Stav Alfi Dec 13 '17 at 20:28
  • IIRC, [this talk by John Lakos](https://youtu.be/QjFpKJ8Xx78) (it's 3 parts; not short) mentions a good pattern on when to forward declare, or at least a good pattern for Bloomberg. – Justin Dec 13 '17 at 20:30
  • @Barry No, I don't need to know anything in the _header_ file about `class1`. – Stav Alfi Dec 13 '17 at 20:30
  • 4
    The disadvantage is that if you decide to rename the class, you won't get any compiler errors if you forget to update the forward declarations. Generally, you'll still have SOME error somewhere, but the cause won't be as obvious. –  Dec 13 '17 at 20:30
  • 2
    A complete class declaration and a forward declaration are not interchangeable: a forward declaration does not let you use members of your class, only declare references and pointers to it. When this is sufficient for your purposes, there are no risks. – Sergey Kalinichenko Dec 13 '17 at 20:30
  • Not necessarily a duplicate question but a possible duplicate answer (if that even makes sense lol) https://stackoverflow.com/questions/4757565/what-are-forward-declarations-in-c – Galik Dec 13 '17 at 20:30
  • For all the downvoters: This question is perfectly clear. __If you think otherwise, you are welcome to comment.__ – Stav Alfi Dec 13 '17 at 20:33
  • 2
    @StavAlfi I didn't down vote but some of the down votes might be because they feel you should have researched forward declarations first. – Galik Dec 13 '17 at 20:34
  • @StavAlfi Regarding the downvotes: Maybe your title is a bit unclear, and you're not giving a specific code example of what you're asking about. Of course replacing an `#include` statement with a plain forward declaration will lead to errors if you try to expand anything more than a reference or pointer with your code seen there. – user0042 Dec 13 '17 at 20:37
  • 2
    @Justin: sadly Lakos’s rules for forward declarations cause a lot of grief and incur substantial costs! On a large code base forward declarations outside the control of the provider of the class are bad. – Dietmar Kühl Dec 13 '17 at 20:45

2 Answers2

13

Forward declarations outside the control of the provider of the class are problematic! I’m working on a code base where a lot of forward declarations are used. While things are initially great the existence of forward declarations become a legacy:

  • Classes can’t be moved from one namespace to a different one. Without the forward declarations the name in the original namespace could be made an alias (typedef or using alias).
  • Classes can’t be turned into specialisations of class templates as is, e.g., useful when generalising a successful class.
  • Class templates cannot be forward declared by users as only the first declaration of a class template can provide default arguments.

Assuming the forward declarations are provided via a header under the control of the class provider/implementer (e.g., the implemented providing something akin to <iosfwd>) these problems are not relevant as there is a central place where the declarations can be changed. However, having users decide to declare entities becomes a legacy causing significant cost.


The approach to provision of declarations outlined above seems to have caused some confusion. I'll try to clarify. In my mind the unit of implementation is a component (which is based on John Lakos's notation of a component). A component defines one or more closely related classes and/or functions. The implementation of a component consists of multiple files:

  1. A header file declaring all relevant entities which also defines entities which must be defined when using the component, i.e., user-accessible classes, enumerations, etc. are defined.
  2. A header file only declaring relevant entities provided by the component (multiple related components may share one such header file; <iosfwd> is an example of such a header shared across multiple components).
  3. An implementation file defining all entities [which are meant to be ODR-used] which are only declared in by the headers above.
  4. At least one file with a test driver testing all entities defined by the component.

Users of a component which in some contexts only need to know about names in the component would include the declaration-only header. In no case would a user provide a declaration of a name in a component: all declaration of a name in a component are the responsibility of the provider/implementer of the component.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Can you please explain what is the "provider of a class" ? – Stav Alfi Dec 13 '17 at 20:47
  • 2
    @StavAlfi The person writing the class that you want to use – Justin Dec 13 '17 at 20:50
  • @StavAlfi: the *provider of a class* is the one who implements the class. That is, forward declarations have to be a part of the implementation of a class, e.g., by having a header with forward declarations of a class or a group of classes. – Dietmar Kühl Dec 13 '17 at 20:52
  • If only forward declarations worked in a more logical way. In cases where I might want a forward declaration, all I want to say is, "There will be a type of this name here." Doesn't matter to me whether that type is by a type alias or something else – Justin Dec 13 '17 at 20:52
  • @Justin: there is a reason why declarations work the way they do, though! Notably, the pointer representation may (and on some systems does) depend on the size of the type. In particular a type may be an alias for `char` or `void` whose pointers have a larger representation than pointers to other types on some systems. – Dietmar Kühl Dec 13 '17 at 20:56
  • @DietmarKühl Yeah I just read through a standards proposal thread on it. I still think that it would be nice to be able to forward declare regardless of what the type is, but it's clearly not an easy problem to solve. – Justin Dec 13 '17 at 21:03
  • 2
    Hahaha, that hat is perfect for you Dietmar. Love it. – Barry Dec 13 '17 at 21:06
  • @DietmarKühl: can you give an example of such system please? – geza Dec 14 '17 at 01:25
  • @geza: you mean a system providing header(s) with declarations? I did: the standard library does this for the streams classes with ``. I’m not awsre of another publicly available system. – Dietmar Kühl Dec 14 '17 at 06:29
  • @DietmarKühl: no, I meant a system, where the pointer representation depends on the size of the type. – geza Dec 14 '17 at 10:39
  • @DietmarKühl "That is, forward declarations have to be a part of the implementation of a class, e.g., by having a header with forward declarations of a class or a group of classes. " I'm confused, a forward declaration must be in the header file or the CPP file? – Stav Alfi Dec 14 '17 at 17:07
  • @geza: I'm not aware of a contemporary system with varying pointer sizes. If I were to go searching I'd look at embedded systems, especially ones with a segmented memory architecture. I briefly used a system with different pointer sizes (i.e., a PC ~30 years ago) although these didn't really differ by type. However, as a result I always considered it plausible enough to not question the possibility. [This answer](https://stackoverflow.com/a/1539196/1120273) seems relevant. – Dietmar Kühl Dec 14 '17 at 17:14
  • @StavAlfi: In my mind the implementation of a C++ "component" (tightly related classes and their supporting functions; there is no agreed upon concept of a *component* in the C++ community, though) uses [at least] four files: 1. a header with definitions; 2. a header with [forward] declarations; 3. an implementation (`.cpp`) file; 4. a file with unit tests for the entities in the component. The first three files are for production use. No user of a component shall declare anything provided by a component but acquire all declarations/definitions via the appropriate header. – Dietmar Kühl Dec 14 '17 at 17:20
2

I can see only the advantage: The header file will have less size.

That's not exactly the point.

Let's assume you have a class declaration in a header file like

namespace MyNamespace {
    class Baz;
}

class Foo {
public:
    void bar(const MyNamespace::Baz & x);
};

and the definition in a separate translation unit as

#include "Baz.hpp"

void Foo::bar(const MyNamespace::Baz & x) {
    // actually do something with Baz
}

and in contrast having everything included in the header file (and necessarily all dependent sources will be recompiled when Baz.hpp will be changed)

#include "Baz.hpp"

class Foo {
public:
    void bar(const MyNamespace::Baz & x);
};

with the declaration, the 1st version might help to compile the code a little bit faster.

Especially if you have your own headers and class declarations, and if any of these are likely to be changed, you only want to recompile your translation units in the codebase, and not every source file that includes your type dependent header.


Note that forward declarations can be only used with references and pointers. Also header inlined code which dereferences to the forwarded type members cannot be used.

user0042
  • 7,917
  • 3
  • 24
  • 39