I use #pragma once
(or you use include guards à la #ifndef...
) basically in every header file in my c++ projects. Is this a coincidence or is it what you find for example in most open source projects (to avoid answers that rely only on personal project experience) . If so, why isn't it the other way around: If I want a header file to be included several times, I use some special pre-processor command and if not I leave the file as is.
Formally, you don't need include guards in your bar. h . It has a single function declaration, which can be repeated many times in one translation units. So, including this header multiple times will not lead to errors.
Header Guard in C++ Header Guards in C++ are conditional compilation directives that help to avoid errors that arise when the same function or variable is defined more than once by the mistake of a programmer. According to C++, when a function or a variable is defined more than once, it yields an error.
In the C and C++ programming languages, an #include guard, sometimes called a macro guard, header guard, or file guard, is a particular construct used to avoid the problem of double inclusion when dealing with the include directive.
Header guards are designed to ensure that the contents of a given header file are not copied, more than once, into any single file to prevent duplicate definitions. This is a good thing because we often need to reference the contents of a given header from different project files.
The behavior of a C++ compiler is specified in terms of how it handles each translation unit. A translation unit is a single file after the preprocessor runs over it. The fact that we have a convention of collecting declarations in certain files and calling them "header" files means nothing to a compiler or the C++ standard.
Simply put, the standard doesn't provide for "header files" so it can't provide for automatically include guarding header files. The standard only provides for the preprocessor directive #include
and the rest is merely convention. Nothing stops you from forward declaring everything and not using header files (except pity for whomever should have to maintain that code...).
So header files aren't special and there's no way to say "that's a header file, guard it", but why can't we guard everything that gets #include
'd? Because #include
is both more and less powerful than a module system like other languages have. #include
causes the preprocessor to paste in other files, not necessarily header files. Sometimes this can be handy if you have the same using and typedef declarations in a bunch of different namespaces in different files. You could collect them in a file and #include
them in a few places. You wouldn't want automatic include guards preventing you from doing that.
Using #ifndef
and #define
to conditionally include headers is also merely convention. The standard has no concept of "include guard". (Modern compilers however actually are aware of include guards. Recognizing include guards can allow faster compilation but it has nothing to do with correctly implementing the standard.)
Getting pedantic
The standard does liberally use the word "header", especially in reference to the C and C++ standard libraries. However, the behavior of #include
is defined under § 16.2 *Source file* inclusion
(emph. mine) and it does not grant any special powers to header files.
There are efforts to get a proper module system into the C++ standard.
Because hysterical raisins.
The C++ preprocessor is almost identical to the C preprocessor, which was designed over 40 years ago. Compilers back then were much simpler. The preprocessor was even simpler, just a dumb macro processor not even a compiler. Although the C++ standard doesn't specify how it works for standard headers, conceptually #include
is still the same as it was 40+ years ago: it causes the preprocessor to insert the contents of the named file into the including file.
In a simple 1970s C codebase without lots of inter-dependencies and sub-modules there may be no need for include guards. Early pre-standard C didn't use function prototypes, which is what the majority of include files are used for these days. If including a header twice caused an error you could probably re-arrange the code to prevent it being included twice.
As codebase grew and became more complex I assume the problem of accidentally including a header twice (probably indirectly, via other headers) became more common. One solution would have been to alter the preprocessor to make it smarter, but that would have required everyone to use the new preprocessor, and would have made it larger and slower. So instead a convention developed which solves the problem using existing features of the preprocessor (#define
and #ifndef
), not by preventing a header being included twice, but by simply making it harmless to include a header twice, because it has no effect after the first time it is included.
Over time the convention became more widely used and is now almost universal, except for the rare examples of headers which are designed to be included twice and written to work correctly that way (e.g. <assert.h>
).
Later still, #pragma once
was introduced by some compilers as an alternative, non-portable way to have the same effect as include guards, but by then there were many thousands of copies of various C preprocessors in use around the word and include guards had become the norm.
So the current behaviour is almost certainly due to historical reasons. Modern languages written today, for today's incredibly powerful computers, would not use something like the C preprocessor if designed from scratch. But C wasn't designed in the 21st Century. I think the include guard convention was established slowly over time, and didn't require any changes to existing software to make it work. Changing it now would break unknown quantities of C and C++ code that rely on the current behaviour and is probably not an option, as backward compatibility is important for both C and C++.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With