This project presents some C++ coding styles that are often used in C++ projects. Furthermore, it also contains my C++ coding style guide.
- Introduction
- Naming conventions
- Comments and documentation
- Formatting
- Header files
- Classes and structs
- Type deduction
- Aliases
- License
- References
A coding style guide is a set of conventions and best practices that standardizes how code is written and formatted. For C++, this means rules for naming conventions, indentation, code structure, commenting, and the use of language features.
C++ is a complex and powerful language with many features that can be used in different ways, which makes adhering to a coding style guide important. A consistent coding style can make C++ code easier to understand, debug, and extend. The following points describe in more detail the importance of using a coding style guide:
- Consistency: when everyone follows the same coding conventions, it becomes easier to collaborate on projects, as the code appears uniform regardless of the individual authors.
- Readability: code that adheres to a style guide is easier to read and understand. Readability is crucial because software development is a team activity, and code often needs to be maintained or extended by someone other than the original author.
- Error reduction: a well-thought-out style guide can help avoid common pitfalls and mistakes, such as inconsistent use of resources, inefficient memory management, or risky language features.
- Best practices: coding style guides often incorporate best practices for writing efficient, modern, and maintainable C++ code.
There's no "official" coding style guide for C++, as we have for Python, for example. However, there are several widely-recognized C++ coding style guides used in the industry. Below are some of the most popular ones:
- C++ Core Guidelines: collection of guidelines written by Bjarne Stroustrup, the creator of C++, along with other C++ experts. It is a set of guidelines for using C++ well, intended to help people to use modern C++ effectively. These guidelines are focused on relatively high-level issues, such as interfaces, resource management, memory management, and concurrency.
- Google C++ Style Guide: comprehensive style guide used across Google's codebase. It covers various topics like naming conventions, function design, and performance optimization.
- Microsoft Coding Style Guide: coding style guide built by Microsoft. It contains many coding style rules that cover, for example, code formatting and naming conventions.
- LLVM Coding Standards: set of conventions used by the LLVM project, focusing on consistency and the clarity of code, particularly for large projects. It includes detailed rules on indentation, function declarations, and code organization.
- Mozilla C++ Style Guide: style guide used for code written within the Mozilla project. It includes guidelines on code organization, documentation, and how to structure complex systems.
This document describes my personal C++ coding style guide, reflecting my preferences and practices. It is not meant to be exhaustive and its focus is not on how to use C++ effectively, for that I highly recommend exploring the C++ Core Guidelines, which is an excellent resource for mastering modern C++ practices and writing clean and efficient code. Instead, this guide highlights some conventions I follow in my C++ projects regarding coding layout style, naming conventions, indentation, etc.
Please note that this coding style guide may not be suitable for every project and should be adapted as needed to meet specific project requirements. It just contains my preferences and conventions that I try to follow in my personal projects and in projects without any established coding standard/style.
Additionally, it is relevant to mention that a C++ coding guide may provide a set of coding rules only for a particular environment. There's no C++ coding standard for all uses and all users. The most important thing is to be consistent within a project, and follow the coding style guide for a given application/project, independently of personal preferences.
This is a non-closed document and may evolve and improve whenever necessary.
This section presents the conventions for naming variables, functions, etc. In some cases, more than one option is presented, so that the reader of this guide can also learn about other styles that are frequently seen in different C++ projects (the first option mentioned for each topic corresponds to the one I prefer most).
However, regardless of the style option you choose, be consistent.
Note: identifier names that contain double underscores __ or that start with an underscore followed by a capital letter (e.g., _Throws) must be avoided. Such identifiers are reserved for the C++ implementation.
Types include the following:
- Classes
- Structs
- Enums
- Type aliases
- Type template parameters
Type names should use PascalCase, i.e., each word should start with a capital letter and no separator (e.g., underscore, _) between words:
class MyClass {
// ...
};
struct MyStruct {
// ...
};
enum class MyEnum {
// ...
};
using IntegerContainer = std::vector<int>;
typedef std::vector<int> IntegerContainer;Another option is using snake_case, i.e., lowercase and with underscores to separate words, as the C++ Standard Library usually applies.
Yet another possibility is to utilize upper case only in the first letter and keep the underscore (e.g., My_type), as mentioned in the C++ Core Guidelines, to better differentiate user-defined types from Standard Library types.
// Using snake_case.
class my_class {
// ...
};
// Using snake_case but with the first letter capitalized.
class My_class {
// ...
};Choose just one style and be consistent within a project.
Variables (including function parameters) and data members of classes and structs are in snake_case:
int a_variable{0};
class MyClass {
private:
int data_member;
};
struct MyStruct {
int data_member;
};When naming variables, it is also common using camelCase, i.e., each word starts with a capital letter, except the first word, and no separator (e.g., underscore) between words:
class MyClass {
private:
int dataMember;
};An aspect that is often seen in C++ projects in variable names is the usage of prefixes and suffixes. For instance, it is common adding the suffix _ or prefixes like m_ or just m for naming class private data members:
class MyClass {
private:
// Using suffix "_" and snake_case.
int data_member_1_;
// Using prefix "m_" and snake_case.
int m_data_member_2;
// Using prefix "m" and camelCase.
int mDataMember3;
};Choose just one style and be consistent within a project.
Variables declared with constexpr or const should use snake_case:
constexpr int a_constexpr_var{42};
const int a_const_var{43};Similarly, it is usually seen the usage of a prefix when naming constants in some C++ projects, such as c or k:
// Using prefix `c` and snake_case.
const int c_const_var{44};
// Using prefix `k` and camelCase.
const int kConstVar{45};Choose just one style and be consistent within a project.
Function names should use snake_case:
void a_free_function(const int a_parameter);
class MyClass {
public:
void a_member_function(const int a_parameter);
};Another possibility is using camelCase or PascalCase for naming functions:
// Using camelCase.
void anotherFreeFunction();
class MyClass {
public:
// Using PascalCase.
void AnotherMemberFunction();
};Choose just one style and be consistent within a project.
Enumerators (for both scoped and unscoped enums) should use snake_case:
enum class MyEnum {
first_enumerator = 0,
second_enumerator,
};Similarly, camelCase or PascalCase might also be used to name enumerators:
enum class MyEnum {
// Using camelCase.
firstEnumerator,
secondEnumerator,
};
enum class MyEnum {
// Using PascalCase.
FirstEnumerator,
SecondEnumerator,
};Choose just one style and be consistent within a project.
Note: never use ALL_CAPS for enumerators to avoid clashes with macros:
#define START 1
#define STOP 2
enum class MyEnum {
// Don't use ALL_CAPS.
START,
STOP
};Namespace names should use snake_case:
namespace project_name {
namespace nested_namespace {
// ...
} // namespace nested_namespace
} // namespace project_nameThe top-level namespace name should be based on the project name. In addition, care must be taken when naming namespaces to avoid collisions between different namespaces but with the same name.
Macro names should use ALL_CAPS, as this is the convention for macros and helps with readability and distinguishability:
#define START_CODE 1Nevertheless, macros can be a source of bugs because they don't obey the usual scope and type rules. Prefer to use Modern C++ features instead (e.g., constexpr) if possible.
Names of files and directories should be all lowercase and include underscores (_) to separate words. Examples:
my_class.cppmy_class.hppa_directory/my_struct.hpp
For files relative to tests, use the following rules:
- For unit tests, use the prefix
ut_. Example: unit test file related tomy_classshould be named asut_my_class.cpp. - For integration tests, use the prefix
it_. Example:it_something.cpp. - For system tests, use the prefix
st_. Example:st_something.cpp. - For testing doubles (mocks, fakes, stubs, etc.), use a prefix that identifies its type. Example: mock file related to
my_classshould be named asmock_my_class.hpp.
Comments are important to state intent more clearly to the reader. It should contain details to help understanding some code, but don't comment what can be easily extracted from the code. While comments are very important, the best code is self-documenting.
Comment using either the // or /* */ syntax, as long as consistency is maintained:
// This is a comment.
// This is a comment in
// multiple lines.
/* This is a comment. */
/*
* This is a comment in
* multiple lines.
*/A TODO comment should be used when something needs to be done or changed in the future. For example, some feature or functionality that should be implemented in the future that currently is missing, or some solution that must be changed/improved because it is now considered as a temporary solution.
Its format must contain the "TODO" text, followed by the issue/bug ID, a name or other identifier that contains the problem referenced by the TODO:
void a_function()
{
// TODO Issue-42: Avoid copies.
// ...code that performs copies...
}It is very useful to document the source code in a format that is understandable by a tool, allowing the verification and parsing of the documentation in an automatic way. Doxygen is a tool that can be utilized for that purpose, so the source code should be documented using the format supported by this tool, as demonstrated in the examples below.
Please refer to the C++ project template to know more details about doxygen, and how this tool can be used in a project.
Examples:
- For files, start with the documentation described as follows:
/** * @file * @copyright Copyright (C) 2024 <Author/Company>. */ // ...
- For classes/structs and member/free functions:
/** * @brief Add the provided two integers. * * @param first_integer First integer to add. * @param second_integer Second integer to add. * * @return Sum of the two integers. */ long add_two_integers(const int first_integer, const int second_integer); /** * @brief My class. */ class MyClass { public: /** * @brief Set the data member value. * * This function verifies if the data can be updated or not. * * @param new_value New data member value. * * @return True if the data member was successfully updated, false otherwise. */ bool set_data(const int new_value); private: /// Data member example. int data_member; }; /** * @brief My struct. */ struct MyStruct { /// Data member example. int data_member; };
- For templates:
/** * @brief Log the provided data. * * @tparam T Data type. * @param data Data to be logged. */ template<typename T> void log(T data); /** * @brief My class. * * @tparam T Data member type. */ template<typename T> class MyClass { private: /// Data member example. T data_member; };
- For enums:
/** * @brief Possible operation results. */ enum class OperationResult { /// Operation has succeeded. success, /// Operation has failed due to out-of-memory. out_of_memory, /// Operation has failed due to unknown reason. unknown };
Ensuring that the code always has the desired format is a very difficult and error-prone task. Thus, it is highly recommended to use a tool that automatically checks and formats the code. A tool that is commonly utilized for this purpose is clang-format, which allows to directly apply a predefined code style (e.g., Google, Microsoft, etc) or customize a new one with the desired style options.
Please refer to the C++ project template to know more details about clang-format, and how this tool can be used in a project.
Each line of code should be at most 100 characters long. A line may exceed this maximum value only in some exceptional cases (e.g., when a literal URL is longer than 100 characters).
Use only spaces, and use 4 spaces for indentation.
Tabs should not be used in code, to ensure that the alignment and number of spaces are consistent independently of the IDE or code editor that is utilized.
Furthermore, it is recommended to configure the IDE/editor to emit spaces when pressing the tab key.
The Kernighan & Ritchie (K&R) style is commonly used for C and C++ code, because it preserves vertical space well, and helps to easily distinguish different language constructs, such as functions and classes.
When adding conventions for constructs not found in C, that becomes what is often called as "Stroustrup" style, being this style suggested in the C++ Core Guidelines.
Example:
namespace some_namespace { // Brace in the same line for namespaces.
// The declarations inside of the namespace should not be indented.
class B : public A { // Brace in the same line for classes/structs/enums.
public: // No indentation.
// ...
};
void some_function()
{ // Brace in a new line for functions.
if (condition) { // Space between "if" and "(", and brace in the same line.
some_function(); // Indentation of the code inside.
}
else if (another_condition) { // "else" is in a new line.
other_function(a);
}
else {
other_function();
}
// Same rules for loops.
for (auto i = 0; i < 10; ++i) {
some_function();
}
while (condition) {
some_function();
}
do {
some_function();
} while (condition);
switch (x) {
case 0: // No indentation of the case label.
// ...
break;
case 1:
// ...
break;
default:
// ...
break;
}
}
} // namespace some_namespaceNote: use always braces as shown in the example for ifs, fors, whiles, etc., to avoid eventual bugs if more code is added later that should be executed inside of the condition/loop scope.
Prefer using the following extensions for header files:
.hpp: for headers that are C++ only. Using this extension communicates that this header should not be included by a C source file, since it contains code that is only compatible with C++..h: for headers that are compatible with C and C++ or pure C headers. This header can be included by both a C source file and a C++ source file.
This distinction can be particularly useful when working on a project that uses both C and C++ code.
But consistency is more important, so if your project uses something different, you should follow that.
To avoid multiple inclusion, the header files should have #define guards. Moreover, to avoid include guards collision, the guard must be unique. For that purpose, the format of the guard name can be something like <PROJECT>_<PATH>_<FILE_NAME>_HPP:
// File "foo/src/dir/bar.hpp":
#ifndef FOO_DIR_BAR_HPP
#define FOO_DIR_BAR_HPP
// Declarations...
#endif // FOO_DIR_BAR_HPPNote: some implementations offer vendor extensions like #pragma once as alternative to #define guards. However, note that this is not standard and is not portable.
Don't write using namespace at global scope in a header file, because it exposes this using namespace to all files that include this header file:
// bad.hpp
#include <iostream>
using namespace std; // Don't do this.To separate the declaration and definition of template classes, the usage of .ipp files is a recommended practice, as shown in the following example:
// my_class.hpp
namespace project_name {
template<typename T>
class MyClass {
// ...
void a_member_function();
};
}
#include "my_class.ipp" // Header file includes the .ipp file.
// my_class.ipp
namespace project_name {
template<typename T>
void MyClass<T>::a_member_function()
{
// ...
}
}Using a .ipp file has some benefits, like separation of concerns, maintainability and code clarity. But it might not be necessary in some cases, such as small or simple templates to avoid unnecessary complexity and file management overhead, and libraries intended for wide distribution to avoid issues with template instantiation.
Thus, the choice between using .ipp file or keeping everything in the header file depends largely on the size and complexity of the code and the project's needs. For more maintainable and scalable codebases, separating template definitions into .ipp files is generally a good practice.
If any member is non-public, use class rather than struct:
struct Example {
int x;
int y;
private:
int z; // Private member: this struct should be declared as class instead.
};Use the following order when declaring a class:
- Types and type aliases:
- Nested structs and classes
- Enums
- Aliases (
using,typedef)
- Static constants
- Factory functions
- Constructors, assignment operators and destructor
- Member functions (static and non-static member functions, and friend functions)
- Data members (static and non-static)
Regarding access control, the following order should be used (omit sections in case they are empty):
publicprotectedprivate
Do not use auto only to avoid the inconvenience of writing an explicit type. Use auto in case if it makes the code clearer to readers or if it makes the code safer.
Prefer using in C++ code instead of typedef:
using Bar = Foo;
typedef Foo Bar; // But prefer `using` instead.Licensed under the MIT license.
- C++ Core Guidelines
- Bjarne Stroustrup: C++ Style and Technique FAQ
- Bjarne Stroustrup: PPP Style Guide
- Google C++ Style Guide
- Microsoft Coding Style Guide
- LLVM Coding Standards
- Mozilla C++ Style Guide
- PEP (Python Enhancement Proposal) 8: Style Guide for Python Code
- Snake case
- Camel case
- C++ project template