guides/coding_style/c++/README.md
Your personal library of every algorithm and data structure code that you will ever encounter
All demonstrates are VALID version
Single line comments should use the standard style (//), and multi line comments should use multiline comments (/* */). There should be a space between the comment delimiter and the comment text.
// Example text
/*
*
* Multiline comment
*
*/
/*
*
* Simple multiline comment
*
* vector synopsis
*
* template <class T, class Allocator = allocator<T> >
* class vector
* {
* public:
* using value_type = T;
* using allocator_type = Allocator;
* }
*/
Try to limit code width to 100 characters.
Split lines as close to the code width as possible (if a parameter is split onto another line, then all of the parameters should be split).
If the width is less than or equal to the maximum code width, then do not split the line (unless split at return type or for readability).
Template definitions should be split.
// n <= 100 columns
bool operator==(Container<Type, Allocator> x, Container<T, Allocator> y);
bool
operator==(Container<Type, Allocator> x, Container<T, Allocator> y);
template<class OtherType, class OtherAllocator>
bool operator==(Container<OtherType, OtherAllocator> x, Container<OtherType, OtherAllocator> y);
template<class OtherType, class OtherAllocator>
bool
operator==(Container<OtherType, OtherAllocator> x, Container<OtherType, OtherAllocator> y);
// n > 100 columns
void cloneTwo(Container<Type, Allocator> foo,
Container<Type, Allocator> bar,
Container<Type, Allocator> baz);
template<class OtherType, class OtherAllocator>
void
cloneTwo(Container<OtherType, OtherAllocator> foo,
Container<OtherType, OtherAllocator> bar,
Container<OtherType, OtherAllocator> baz);
* ** and references & && *& should be part of the variable/function name (even if variable name is not declared) or qualifier. Add a space between previous types or qualifiers (unless **, &&, and *&).// pointers and references
int a = 0;
int *pa = &a;
int *&rpa = pa;
int **ppa = &pa;
int &&rr = 0;
bool operator==(int *x, int *y);
bool operator==(int *const *x, int *const *y);
bool operator==(const int **const x, const int **const y);
bool &getReference();
bool *&getPointer();
bool *const getConstPointer();
bool *const &getReferenceOfConstPointer();
// even if variable name is not declared
bool operator==(int *&, int *&);
bool operator==(int &&, int &&);
const qualifier should be on the right hand side of its target but to the left of the type.class Dummy
{
const int value() const;
void value(const int *const v);
int *getPointer();
const int *const *getOtherPointer() const;
};
void foo()
{
bool res = true;
short i = 1;
long sum_short = 1;
while (i != 0 && ++i)
sum_short += i;
if (i == 0 && (0 == i))
res &= true;
else
res &= false;
if ((1 != 0) &&
(1 == true || 0 == false))
res &= true;
else
res &= false;
if (res != true ||
(!(res == true) ||
(res == false)))
cout << "wrong\n";
}
Put one space:
After commas.
Between operators.
Between condition and its opening brace.
Around initializer of class constructor;
Remove spaces:
In whitespace-only line.
After opening brace/parenthese, and before closing brace/parenthes.
Between empty braces, and parentheses.
Be clear, the demo not follow the Newline rules:
while () // 1 space between while and '(', 0 space between parentheses.
{ // 0 space after opening brace-only line.
if (true) // 1 space between if and '('.
{
ClassName obj{1, 2, 3}; // 0 space after '{', before '}'.
int i = (1 + 1) * 1; // 1 space around '=', '+', and '*'.
doSomthing(obj, i, 1); // 1 space after commas.
}
} // 0 space after closing brace-only line.
Use only spaces, and indent 4 spaces at a time.
We use spaces for indentation. Do not use tabs in our code.
bool predicateFunc(vector<int> vec) {
for (; condition; ) // 4 spaces
if (condition) // 8 spaces
return false; // 12 spaces
// none space
return true; // 4 spaces
}
Add newline:
Between two functions, classes, and conditions.
After #define, #include.
Between preconditions and its scope (ignore between preconditions).
#include <iostream>
#ifndef DDD // no newline between #ifndef and #define line
#define DDD
int func1();
int func2()
{
int a{1}, b{0};
if (a == b)
// ...
while (a - b > 0)
// ...
}
class A;
class B
{
public:
int func1();
int func2()
{
// ...
}
private:
int v1;
bool v2;
int func3();
}
#endif
When using braces, put them on their own lines. When loops or if statements have only one statement, omit the braces (unless between do and while on do-while statements or to disambiguate a dangling else).
int main()
{
if (condition)
{
statement;
statement;
}
else
statement;
if (condition)
{
if (condition)
statement;
else
statement;
}
for (;condition;)
statement;
do
{
statement;
} while (condition);
}
Names should be descriptive and avoid abbreviations, unless the abbreviation is commonly known.
int errorFlag;
int currentIter; // the certain universally-known abbrs ("Iter") are OK
int numberDnsConnections; // ok, most people know what "DNS" stands for
C++ file should end in .cpp and header file should end in .hpp (do not end in .h).
Types should be named like PascalCase.
using VecI = std::vector<int>;
template<typename _T>
using VecTwo = std::vector<std::vector<_T>>;
class PascalCase;
struct PascalCase;
union PascalCase;
enum PascalCase;
enum class PascalCase;
Local variables should be named using standard camelCase.
int name;
int camelCase;
Global variables should be avoided for the most part. If they are necessary, then they should be named using prefix 'g_' and standard camelCase.
int g_name;
int g_camelCase;
Class members should be private, add an underscore to the end of their name.
class PascalCase;
{
int name_;
int camelCase_;
};
Constant variables should be named using PascalCase.
const int Name;
constexpr int VariableName;
enum class ExampleEnum
{
One,
TwoTwo
};
Functions should be named using standard camelCase.
int name();
int camelCase();
class PascalCase
{
int doSomething();
};
When naming macros, use uppercase for all the letters, and separate words by underscores.
#define VALID
#define UPPERCASE_VALID
If you are naming something that is analogous to an existing C++ entity then you can follow the existing naming convention scheme.
sparse_hash_set<> ... // STL-like entity; follows STL naming conventions
long BUFFER_MAX ... // a constant, as in INT_MAX
using diff_t ... // as in ptrdiff_t
When including header files, only include files that are portable across all compilers (Unless where applicable). Do not include <bits/stdc++.h>.
Pay attention to whether the header is not in use, and do not include it if so. If only the implementation uses it, then include the file in .cpp, not .hpp.
// sample1.hpp
#include <vector> // ok, return type is vector
template<typename Ty>
std::vector<Ty> toVector(Ty t[], size_t sz);
// sample2.hpp
class ListHelp
{
void sort();
};
// sample2.cpp
#include "sample2.hpp"
#include <utility> // ok, only use the swap function in implementation
using namespace std;
void ListHelp::sort()
{
// use the swap function
}
When using namespaces, pay attention to name collisions/name conflicts:
In header files, do not use a using directive or declaration.
// sample1.hpp
#include <vector>
// ok, if users include this file, they also need to declare std namespace to use member of vector
template<typename Ty>
std::vector<Ty> toVector(Ty t[], size_t sz);
// sample2.hpp
class ListHelp
{
void sort();
}
// sample2.cpp
#include "sample2.hpp"
#include <utility>
using namespace std; // ok, users will include .hpp not .cpp
void ListHelp::sort()
{
// may use the swap function
}
When listing POD (Plain Old Data) types, pass them by value. When listing user-defined types, pass them by const reference. When a function has to modify a variable, then, and only then pass the variable by reference.
void exampleFunction(int pod, const Example& object, char& modifiableChar)
{
// ...
}
When listing members of a class, if possible list them in the order public, private, then protected. If using using for aliases, or using base class constructors, then the order can be broken. In this case, if possible, try and list the public interface of the class first.
Encapsulation labels should be indented to the same level as the class. Add a new line after the end of each label for readability.
class Example
{
private:
using Int = int;
protected:
using typename BaseClass::size_type;
public:
// ...
private:
// ...
protected:
// ...
};
When listing member functions of a class, first list the main constructor, then secondary constructors, and then copy assignment/copy construction/move assignment/move construction functions. After this, add the destructor. Then, add a new line, and then list all members of the class.
struct keyword, otherwise use the class keyword.class Example
{
public:
Example() { }
Example(int value) { }
Example(const Example& other) { }
~Example()
void memberFunction();
private:
int value_;
};
When declaring a constructor, use a member initializer list to initialize class members.
class Example
{
public:
Example(int a, int b) : a_{a}, b_{b} { }
private:
int a_, b_;
};
When dealing with enumerations, use enum class, not enum (Unless dealing with unnamed enumerations or C style code).
Warning: if you dealing with low-level operation, and need use explicit type convertion, you must be careful about underlying type (default is the int it at least 2-bytes).
When dealing with aliases, use using, not typedef.
Don't put alias in public, unless the aliases is guaranteed to always be the same as the type it's currently aliased to.
The using declaration can be read almost as an English sentence.
More easily declare an alias with template.