SF: Source files
Distinguish between declarations (used as interfaces) and definitions (used as implementations). Use header files to represent interfaces and to emphasize logical structure.
Source file rule summary:
(SF.2: A
.h
file must not contain object definitions or non-inline function definitions)(SF.3: Use
.h
files for all declarations used in multiple source files)(SF.4: Include
.h
files before other declarations in a file)(SF.5: A
.cpp
file must include the.h
file(s) that defines its interface)(SF.7: Don't write
using namespace
at global scope in a header file)(SF.21: Don't use an unnamed (anonymous) namespace in a header)
(SF.22: Use an unnamed (anonymous) namespace for all internal/non-exported entities)
SF.1: Use a .cpp
suffix for code files and .h
for interface files if your project doesn't already follow another convention
Reason
It's a longstanding convention. But consistency is more important, so if your project uses something else, follow that.
Note
This convention reflects a common use pattern:
Headers are more often shared with C to compile as both C++ and C, which typically uses .h
,
and it's easier to name all headers .h
instead of having different extensions for just those headers that are intended to be shared with C.
On the other hand, implementation files are rarely shared with C and so should typically be distinguished from .c
files,
so it's normally best to name all C++ implementation files something else (such as .cpp
).
The specific names .h
and .cpp
are not required (just recommended as a default) and other names are in widespread use.
Examples are .hh
, .C
, and .cxx
. Use such names equivalently.
In this document, we refer to .h
and .cpp
as a shorthand for header and implementation files,
even though the actual extension might be different.
Your IDE (if you use one) might have strong opinions about suffixes.
Example
// foo.h:extern int a; // a declarationextern void foo();// foo.cpp:int a; // a definitionvoid foo() { ++a; }
foo.h
provides the interface to foo.cpp
. Global variables are best avoided.
Example, bad
// foo.h:int a; // a definitionvoid foo() { ++a; }
#include <foo.h>
twice in a program and you get a linker error for two one-definition-rule violations.
Enforcement
- Flag non-conventional file names.
- Check that
.h
and.cpp
(and equivalents) follow the rules below.
SF.2: A .h
file must not contain object definitions or non-inline function definitions
Reason
Including entities subject to the one-definition rule leads to linkage errors.
Example
// file.h:namespace Foo { int x = 7; int xx() { return x+x; }}// file1.cpp:#include <file.h>// ... more ... // file2.cpp:#include <file.h>// ... more ...
Linking file1.cpp
and file2.cpp
will give two linker errors.
Alternative formulation: A .h
file must contain only:
#include
s of other.h
files (possibly with include guards)- templates
- class definitions
- function declarations
extern
declarationsinline
function definitionsconstexpr
definitionsconst
definitionsusing
alias definitions- ???
Enforcement
Check the positive list above.
SF.3: Use .h
files for all declarations used in multiple source files
Reason
Maintainability. Readability.
Example, bad
// bar.cpp:void bar() { cout << "bar\n"; }// foo.cpp:extern void bar();void foo() { bar(); }
A maintainer of bar
cannot find all declarations of bar
if its type needs changing.
The user of bar
cannot know if the interface used is complete and correct. At best, error messages come (late) from the linker.
Enforcement
- Flag declarations of entities in other source files not placed in a
.h
.
SF.4: Include .h
files before other declarations in a file
Reason
Minimize context dependencies and increase readability.
Example
#include <vector>#include <algorithm>#include <string>// ... my code here ...
Example, bad
#include <vector>// ... my code here ...#include <algorithm>#include <string>
Note
This applies to both .h
and .cpp
files.
Note
There is an argument for insulating code from declarations and macros in header files by #including
headers after the code we want to protect
(as in the example labeled "bad").
However
- that only works for one file (at one level): Use that technique in a header included with other headers and the vulnerability reappears.
- a namespace (an "implementation namespace") can protect against many context dependencies.
- full protection and flexibility require modules.
See also:
Enforcement
Easy.
SF.5: A .cpp
file must include the .h
file(s) that defines its interface
Reason
This enables the compiler to do an early consistency check.
Example, bad
// foo.h:void foo(int);int bar(long);int foobar(int);// foo.cpp:void foo(int) { /* ... */ }int bar(double) { /* ... */ }double foobar(int);
The errors will not be caught until link time for a program calling bar
or foobar
.
Example
// foo.h:void foo(int);int bar(long);int foobar(int);// foo.cpp:#include <foo.h>void foo(int) { /* ... */ }int bar(double) { /* ... */ }double foobar(int); // error: wrong return type
The return-type error for foobar
is now caught immediately when foo.cpp
is compiled.
The argument-type error for bar
cannot be caught until link time because of the possibility of overloading, but systematic use of .h
files increases the likelihood that it is caught earlier by the programmer.
Enforcement
???
SF.6: Use using namespace
directives for transition, for foundation libraries (such as std
), or within a local scope (only)
Reason
using namespace
can lead to name clashes, so it should be used sparingly.
However, it is not always possible to qualify every name from a namespace in user code (e.g., during transition)
and sometimes a namespace is so fundamental and prevalent in a code base, that consistent qualification would be verbose and distracting.
Example
#include <string>#include <vector>#include <iostream>#include <memory>#include <algorithm>using namespace std;// ...
Here (obviously), the standard library is used pervasively and apparently no other library is used, so requiring std::
everywhere
could be distracting.
Example
The use of using namespace std;
leaves the programmer open to a name clash with a name from the standard library
#include <cmath>using namespace std;int g(int x){ int sqrt = 7; // ... return sqrt(x); // error}
However, this is not particularly likely to lead to a resolution that is not an error and
people who use using namespace std
are supposed to know about std
and about this risk.
Note
A .cpp
file is a form of local scope.
There is little difference in the opportunities for name clashes in an N-line .cpp
containing a using namespace X
,
an N-line function containing a using namespace X
,
and M functions each containing a using namespace X
with N lines of code in total.
Note
(Don't write using namespace
at global scope in a header file).
Enforcement
Flag multiple using namespace
directives for different namespaces in a single source file.
SF.7: Don't write using namespace
at global scope in a header file
Reason
Doing so takes away an #include
r's ability to effectively disambiguate and to use alternatives. It also makes #include
d headers order-dependent as they might have different meaning when included in different orders.
Example
// bad.h#include <iostream>using namespace std; // bad// user.cpp#include "bad.h"bool copy(/*... some parameters ...*/); // some function that happens to be named copyint main(){ copy(/*...*/); // now overloads local ::copy and std::copy, could be ambiguous}
Note
An exception is using namespace std::literals;
. This is necessary to use string literals
in header files and given the rules - users are required
to name their own UDLs operator""_x
- they will not collide with the standard library.
Enforcement
Flag using namespace
at global scope in a header file.
SF.8: Use #include
guards for all .h
files
Reason
To avoid files being #include
d several times.
In order to avoid include guard collisions, do not just name the guard after the filename. Be sure to also include a key and good differentiator, such as the name of library or component the header file is part of.
Example
// file foobar.h:#ifndef LIBRARY_FOOBAR_H#define LIBRARY_FOOBAR_H// ... declarations ...#endif // LIBRARY_FOOBAR_H
Enforcement
Flag .h
files without #include
guards.
Note
Some implementations offer vendor extensions like #pragma once
as alternative to include guards.
It is not standard and it is not portable. It injects the hosting machine's filesystem semantics
into your program, in addition to locking you down to a vendor.
Our recommendation is to write in ISO C++: See (rule P.2).
SF.9: Avoid cyclic dependencies among source files
Reason
Cycles complicate comprehension and slow down compilation. They also complicate conversion to use language-supported modules (when they become available).
Note
Eliminate cycles; don't just break them with #include
guards.
Example, bad
// file1.h:#include "file2.h"// file2.h:#include "file3.h"// file3.h:#include "file1.h"
Enforcement
Flag all cycles.
SF.10: Avoid dependencies on implicitly #include
d names
Reason
Avoid surprises.
Avoid having to change #include
s if an #include
d header changes.
Avoid accidentally becoming dependent on implementation details and logically separate entities included in a header.
Example, bad
#include <iostream>using namespace std;void use(){ string s; cin >> s; // fine getline(cin, s); // error: getline() not defined if (s == "surprise") { // error == not defined // ... }}
<iostream>
exposes the definition of std::string
("why?" makes for a fun trivia question),
but it is not required to do so by transitively including the entire <string>
header,
resulting in the popular beginner question "why doesn't getline(cin,s);
work?"
or even an occasional "string
s cannot be compared with ==
").
The solution is to explicitly #include <string>
:
Example, good
#include <iostream>#include <string>using namespace std;void use(){ string s; cin >> s; // fine getline(cin, s); // fine if (s == "surprise") { // fine // ... }}
Note
Some headers exist exactly to collect a set of consistent declarations from a variety of headers. For example:
// basic_std_lib.h:#include <string>#include <map>#include <iostream>#include <random>#include <vector>
a user can now get that set of declarations with a single #include
#include "basic_std_lib.h"
This rule against implicit inclusion is not meant to prevent such deliberate aggregation.
Enforcement
Enforcement would require some knowledge about what in a header is meant to be "exported" to users and what is there to enable implementation. No really good solution is possible until we have modules.
SF.11: Header files should be self-contained
Reason
Usability, headers should be simple to use and work when included on their own. Headers should encapsulate the functionality they provide. Avoid clients of a header having to manage that header's dependencies.
Example
#include "helpers.h"// helpers.h depends on std::string and includes <string>
Note
Failing to follow this results in difficult to diagnose errors for clients of a header.
Note
A header should include all its dependencies. Be careful about using relative paths because C++ implementations diverge on their meaning.
Enforcement
A test should verify that the header file itself compiles or that a cpp file which only includes the header file compiles.
SF.12: Prefer the quoted form of #include
for files relative to the including file and the angle bracket form everywhere else
Reason
The standard provides flexibility for compilers to implement
the two forms of #include
selected using the angle (<>
) or quoted (""
) syntax. Vendors take
advantage of this and use different search algorithms and methods for specifying the include path.
Nevertheless, the guidance is to use the quoted form for including files that exist at a relative path to the file containing the #include
statement (from within the same component or project) and to use the angle bracket form everywhere else, where possible. This encourages being clear about the locality of the file relative to files that include it, or scenarios where the different search algorithm is required. It makes it easy to understand at a glance whether a header is being included from a local relative file versus a standard library header or a header from the alternate search path (e.g. a header from another library or a common set of includes).
Example
// foo.cpp:#include <string> // From the standard library, requires the <> form#include <some_library/common.h> // A file that is not locally relative, included from another library; use the <> form#include "foo.h" // A file locally relative to foo.cpp in the same project, use the "" form#include "foo_utils/utils.h" // A file locally relative to foo.cpp in the same project, use the "" form#include <component_b/bar.h> // A file in the same project located via a search path, use the <> form
Note
Failing to follow this results in difficult to diagnose errors due to picking up the wrong file by incorrectly specifying the scope when it is included. For example, in a typical case where the #include ""
search algorithm might search for a file existing at a local relative path first, then using this form to refer to a file that is not locally relative could mean that if a file ever comes into existence at the local relative path (e.g. the including file is moved to a new location), it will now be found ahead of the previous include file and the set of includes will have been changed in an unexpected way.
Library creators should put their headers in a folder and have clients include those files using the relative path #include <some_library/common.h>
Enforcement
A test should identify whether headers referenced via ""
could be referenced with <>
.
SF.20: Use namespace
s to express logical structure
Reason
???
Example
???
Enforcement
???
SF.21: Don't use an unnamed (anonymous) namespace in a header
Reason
It is almost always a bug to mention an unnamed namespace in a header file.
Example
// file foo.h:namespace{ const double x = 1.234; // bad double foo(double y) // bad { return y + x; }}namespace Foo{ const double x = 1.234; // good inline double foo(double y) // good { return y + x; }}
Enforcement
- Flag any use of an anonymous namespace in a header file.
SF.22: Use an unnamed (anonymous) namespace for all internal/non-exported entities
Reason
Nothing external can depend on an entity in a nested unnamed namespace. Consider putting every definition in an implementation source file in an unnamed namespace unless that is defining an "external/exported" entity.
Example; bad
static int f();int g();static bool h();int k();
Example; good
namespace { int f(); bool h();}int g();int k();
Example
An API class and its members can't live in an unnamed namespace; but any "helper" class or function that is defined in an implementation source file should be at an unnamed namespace scope.
???
Enforcement
- ???