summaryrefslogtreecommitdiff
path: root/cxx-style.txt
blob: 8e968b803e9ca3df6735e39e0a0a0e17f2569dd5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
0. Don't try to be clever, prefer value/move semantics

These days optimizers can often "see through" (via constexpr/inline/lto) and
optimize simple code that uses value semantics to the equivalent (or even
better) "clever" code that tries to avoid extra copies, allocations, etc.

See also the note on small value optimization.

1. Modernization

- use override
- consider constexpr for inline functions (e.g., enum class bitmask operators)
- consider noexcept
? migrate to #pragma once

2. Almost never auto

Using auto instead of the actual type often makes code harder to understand.
You may (but don't have to) use auto when (a) the type is spelled out on the
right hand side (e.g., casts, make_*<>() functions, etc), and (b) in cases of
idiomatic use where the type is clear to anyone familiar with C++ and it would
be painful to spell it out explicitly.

Examples of the latter are lambda initializations, iterator initializations
(e.g, from begin()), and some cases of pair initialization (e.g, from
container's insert()).

3. Almost never brace-initialization

We only use brace-initialization syntax when initializing an aggregate or a
container. We can also use it for an aggregate-like initialization of an
aggregate-like class.

An aggregate-like class is class with public data members only and an
aggregate-like initialization is an initialization that provides initializers
for every such data member. For example:

struct foo
{
  int x, y;

  foo (int x, int y);
  foo (int both);
};

foo x {0, 1}; // Ok.
foo x {0};    // Bad.

For default member variable initialization use assignment syntax, for example:

struct foo
{
  int i = 123;
  string s = string (123, 's');
};

4. Many performance-critical standard types are "small object optimized"

For example, across all the implementations that we care, std::string can hold
at least 15 characters without allocation. Similarly, std::function can hold a
lambda with at least 2 pointers also without allocation.

As a result, it is seldom makes sense to resort to elaborate optimizations
such as string pooling. In fact, if we have a string pool that contains mostly
short, SOO strings, then we will most likely hurt performance due to lack of
locality.