C++ Cheatsheet

kuniga.me > Docs > C++ Cheatsheet

C++ Cheatsheet

Syntax for common tasks I run into often. Assumes C++17.

Index

Basic Types

Array

Literal initialization:

int int_array[5] = { 1, 2, 3, 4, 5 };

Array of objects, initialize later:

MyClass class_array[3];
MyClass c;
class_array[1] = c;

Array vs std::array

std::array() has friendlier semantics (discussion).

Conversions

int to string

#include <string>
std::string = to_string(10);

Enum

Strong enumeration, scoped enumeration

enum class MyBoolean { kYes, kNo };

Unscoped enumeration (without class modifier):

enum MyBoolean { kYes, kNo };

Assigning

(scoped enum)

MyBoolean b = MyBoolean::kYes;

Getting value

(scoped enum)

MyBoolean b = MyBoolean::kYes;
cout << static_cast<bool>(my_enum) << endl;

Optional

Create:

// None
std::optional<std::string> x = std::nullopt;
// Value
std::optional<std::string> y("hello");

Access:

std::cout << *y << std::endl;

Check:

std::optional<int> y = 0;
if (!y) {
  std::cout << "none" << std::endl;
} else {
  std::cout << *y << std::endl;
}

Pair

Create:

#include <utility>
auto p = std::make_pair(10, true);

Destructuring assigment:

std::pair<int, std::string> getPair();

int first;
std::string second;
std::tie(first, second) = getPair();

NOTE: Works with tuples too.

Ignoring one of the members in the pair:

std::tie(std::ignore, second) = getPair();

Strings

Literal

The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time.

constexpr char s[] = "some_constant";

View

Optimization for read-only strings which does not make copies on assignment.

std::string_view str1{"my_string"};
std::string_view str2{ str2 }; // No copy

Sstream

Osstream

Construct streams using the << syntax.

#include <sstream>
#include <iostream>

std::ostringstream ss;
ss << "hello";
ss << " world";
std::cout << ss.str() << std::endl;

Struct

struct MyStruct {
    int x;
    double z;
}

Initialization:

MyStruct my_struct = {.x = 1, .z = 0.5};

Type Alias

using MyNewType = std::vector<std::string>;

Collections

Hash Map

In C++ unordered_map implements a hash map. Search, insertion, and removal of elements have average constant-time complexity. Keys don’t have any particular order. map keeps the keys sorted.

Import

#include <unordered_map>

Initialization

Empty map:

std::unordered_map<std::string, int> h;

std::unordered_map<std::string, int> m = { {'a', 1}, {'b', 2} };

Access

std::cout << h["key"] << std::endl;

Emplace

h.emplace("key", 100);

Can be used to avoid the creation of temporary objects.

Warning: only adds to map if the corresponding key does not exist.

Insert

h["key"] = 100;

Iterating

x is a pair (key, value).

for (auto [key, value]:h) {
    std::cout << key << ", " << value << std::endl;
}

C++20 and after:

bool has_key = h.contains("key");

Before:

bool has_key = h.find("key") != h.end();

Set

Prefer std::unordered_set over std::set.

Initialization

From literals:

std::unordered_set<int> s = {4, 16, 9, 25};

Membership

Returns 1 if element exists, 0 otherwise. Faster than .find().

s.count(element);

Remove

Remove entry (in-place):

s.erase(value);

Tuples

Include:

#include <tuple>

Initialization

std::tuple<int, std::string> t = std::make_tuple(1, "hello");

or:

auto t = std::make_tuple<int, std::string>(1, "hello");

Access

std::get<n>. Example:

std::cout << std::get<0>(t); // 1
std::cout << std::get<1>(t); // "hello"

Concatenate

std::tuple_cat. Example:

auto t = std::make_tuple<int, std::string>(1, "a");

auto u1 = std::make_tuple<int>(1);
auto u2 = std::make_tuple<std::string>("a");
auto u = std::tuple_cat(u1, u2);

assert (t == u);

Variadic

Using std::apply, lambdas and variadic. Example: handle tuple in a generic fashion:

// Let t be any generic tuple. t1 is a copy of t
auto t1 = std::apply(
  [](auto... args) {
    return std::make_tuple(desc...);
  },
  t
);

Vector

Initialization

std::vector<int> vec{10, 20, 30};

Iterate

for(auto e:vec) {
    std::cout << e << std::endl;
}

Empty

vec.empty();

Size

v.size();

Slice

Getting a subset of a vector for index a to index b:

std::vector<int>(v.begin() + a, v.begin() + b + 1);

If b = v.size() -1:

std::vector<int>(v.begin() + a, v.end());

Passing as parameter

void f(std::vector<int> v) {}

// Type deduction
f({1, 2});

// Explicit type
f(std::vector<int> {1, 2});

Memory

Shared Pointers

Creating pointers:

struct C {
  C(int i) : i(i) {}
  int i;
}
auto pt = std::make_shared<C>(1); // overload (1)

De-referencing is the same as a regular pointer:

std::shared_ptr<int> pt (new int);
std::cout << *pt << std::endl;

Placement New

Allows pre-allocating into a larger chunk of memory into a buffer and then newing objects into that memory.

char *buf = new char[10000];
C *p1 = new (buf) C(1);
C *p2 = new (buf + sizeof(*p1)) C(2);
delete [] buf;

Note that deletion is not performed for the placement new, only for the original buffer.

Flow Control

Conditionals

Initialize and evaluate inline:

if (bool x = false; !c) {
  cout << "prints" << endl;
}

Exceptions

Basic try/catch:

try {
  throw std::runtime_error("Error");
} catch (const std::exception& e) {
    std::cout << e.what() << "\n";
}

Custom exception:

class CustomException: public std::exception
{
  virtual const char* what() const throw() {
    return "My exception";
  }
} custom_exception;

throw custom_exception;

Object-Oriented

Class

Constructor

Defining constructor outside class definition:

// Point.hpp
class Point {
    private:
        int x;
        int y;
    public:
        Point();
};

// Point.cpp
Point::Point(void) {
    x = 0;
    y = 0;
}

Point::Point(int _x, int _y) {
    x = _x;
    y = _y;
}

Explicit initialization:

Point::Point(int x, int y): x(x), y(y)  {}

Destructor

// Point.hpp
class Point {
    ...
    public:
        ~Point();
};

// Point.cpp
Point::~Point(void) {
    // Clean up allocated memory
}

Instantiate

When to use new:

// Automatically destroyed when leaving scope
Point point(10, 20);

// Only destroyed when called w/ delete
Point point = new Point(10, 20);

Methods

Read-only method. Add const after function signature.

struct C {
  void my_method() const {
    // Cannot modify x ()
  }
  int x;
};

Inheritance

class Base {
};

class Child : public Base {

}

// Parent constructor needs to be called
// first thing. If processing is need before
// that, can call a function that returns an
// argument to Base()
Child::Child(void) : Base() {
}

Visibility

One of public|protected|private (default is private). Makes all methods from Base the most restrictive between the inheritance visibility and the original method visibility.

class Base {
protected:
    void f() {};
};

// ChildPrivate::f() is private
class ChildPrivate : Base {}
// ChildPublic::f() is protected
class ChildPublic : public Base {}

For struct the inheritance mode is public by default.

class Base {
  public:
  void f() {};
};

// ChildStruct::f() is public
struct ChildStruct : Base {};

Abstract methods

If you’re coming from Java, C++ version of abstract methods are pure virtual methods.

class Base {
    virtual void abstract() = 0;
};

Interface

If you’re coming from Java, C++ doesn’t have syntax for interfaces but it can be modeled as class with all pure virtual methods.

class Interface {
    virtual void foo() = 0;
    virtual int bar() = 0;
    virtual ~Interface() {};
};

Note that we need to define the virtual destructor in this case.

Override methods

By default C++ binds the methods to the type, even if it was re-implemented in the derived class. Example:

struct Base {
  void f() { printf("base\n"); };
};

struct Child : Base {
  void f() { printf("child\n"); };
};

void g(Base *x) { x->f(); }
auto x = new Child();
g(x); // prints "base"

In g(), f() is bound to Base (type) not to Child (instance). To change this behavior, f() must be virtual in Base:

struct Base {
  virtual void fv() { printf("base\n"); };
};

struct Child : Base {
  void fv() { printf("child\n"); };
};

void g(Base *x) { x->f(); }
auto x = new Child();
g(x); // prints "child"

It’s a good practice to add override so that the compiler catches this subtlety:

struct Base {
  void f() {};
  virtual void fv() {};
};

struct Child : Base {
  // Error: Base::f() is not virtual
  void f() override {};
  void fv() override {}
};

You can also add override when implementing pure virtual methods.

Templates

Function

template <typename T>
T id(T x) {
   return x;
}

Class

template <typename T>
struct Wrapper {
    T v_;
public:
    Wrapper(T v) {
      v_ = v;
    }
    T get() {
      return v_;
    }
};

Inner Type

It’s possible to access the type of a template:

template <typename T>
struct Wrapper {
    T v_;
    using TInner = T;
};

struct WrapperInt : Wrapper<int> {
  int get() {
    return 0;
  }
};
struct WrapperStr : Wrapper<std::string> {
  std::string get() {
    std::string s = "str";
    return s;
  }
};

template <typename T, typename TType = typename T::TInner>
TType getter(T val) {
  return val.get();
}

Specialization

Type alias

template <typename T>
struct Generic {
  T v_;
};

using Int = Generic<int>;

Implementation

In this setup the generic class acts almost like a interface.

template <typename T>
struct Cast {
  static T cast(void *ptr);
};

template <>
struct Cast<bool> {
  static bool cast(void *ptr) {
    bool b = (bool*)ptr;
    return b;
  }
};

template <>
struct Cast<int> {
  static int cast(void *ptr) {
    int* b = (int*)ptr;
    return *b;
  }
};

template <typename T>
T factory(void *ptr) {
  return Cast<T>::cast(ptr);
}

bool b = true;
void *ptr = &b;

auto c = factory<bool>(ptr);
auto d = factory<int>(ptr);
// Linker error:
// Undefined symbols for architecture
auto e = factory<float>(ptr);

Variadic

The generic class can use typename ...T to allow specialization by any number of types:

template <typename ...T>
struct C {};

template <>
struct C<bool> {};

template <>
struct C<int, int> {};

C<int, int> pair_int;
C<bool> boolean;

Compile Time

Static Assert

Syntax:

static_assert(<expr>, "message");

Where <expr> must be a constant expression.

Constant Expressions

Syntax

Function:

constexpr int factorial(int n) {
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

Class member:

struct C {
  static constexpr int my_expr = 3 + 4;
};

Type Traits

The working unit of a type trait is the type trait std::integral_constant, which wraps a constant and its type. Example:

typedef std::integral_constant<int, 2> two_t;

Access the value:

std::cout << two_t::value << std::endl;

A boolean type trait is so common it has its own alias:

template <bool B>
using bool_constant = integral_constant<bool, B>;

Type Equality (=)

Done via std::is_same():

template <typename T>
void is_int() {
   if (std::is_same<T, std::int32_t>::value) {
     std::cout << "is int" << std::endl;
   } else {
     std::cout << "isn't" << std::endl;
   }
}
factory<int>(); // is int
factory<double>(); // isn't

Conjunction (&&)

Check multiple types: std::conjunction(). It expects one or more std::bool_constant types:

template<typename T, typename... Ts>
void f() {
  if (std::conjunction<std::is_same<T, Ts>...>::value) {
    std::cout << "all types are the same\n" << std::endl;
  } else {
    std::cout << "not all types are the same" << std::endl;
  }
}

f<int, int>(); // same
f<int, bool>(); // not

Files

Check if file exists

Works for directories too.

#include <filesystem>
namespace fs = std::filesystem;

bool file_exists = fs::exists("path");

Create directory

#include <filesystem>
namespace fs = std::filesystem;

fs::create_directory("path");

Create temporary file name

std::string filename = std::tmpnam(nullptr);

Read from file

#include <fstream>

std::ifstream my_file("filename");
if (my_file.is_open()) {
    while (getline(my_file, line)) {
        std::cout << line << std::end;
    }
}

Write to file

#include <fstream>

std::ofstream my_file("filename");
if (my_file.is_open()) {
    my_file << "a line" << std::endl;
}
my_file.close();

Date and Time

Time

Duration

Structure: std::chrono::duration<Rep, Period>. Rep is the underlying type, e.g. double or int. Period is a rational number (std::ratio) indicating the relative scale factor to 1 second and is only relevant when converting between units.

Examples

// 1 tick = 1 second
std::chrono::duration<int, std::ratio<1> sec(1);

// 1 tick = 60 seconds
std::chrono::duration<int, std::ratio<60>> min1(1);
// 60 ticks = 60 seconds
std::chrono::duration<int, std::ratio<1>> min2(60);
// comparison handles different periods
assert (min1 == min2);

// 30 ticks = 30 ms
std::chrono::duration<int, std::ratio<1, 1000>> ms(30);

Helper duration types

The actual underlying Rep for these types is open for compilers to determine but the spec dictates signed integers of a minimum size (shown in parenthesis below, in bits).

Type Bits
std::chrono::nanoseconds 64
std::chrono::microseconds 55
std::chrono::milliseconds 45
std::chrono::seconds 35
std::chrono::minutes 29
std::chrono::hours 23

Some of the examples from above simplified:

// 1 tick = 1 second
std::chrono::seconds sec(1);

// 1 tick = 60 seconds
std::chrono::minutes min1(1);

Get value from duration

It always returns the value in the scale defined by Period, so basically the value we passed when constructing it:

std::chrono::duration<double, std::ratio<1, 30>> hz30(3.5)
cout << hz30.count() << endl; // 3.5

Convert between units

Syntax: std::chrono::duration_cast<duration_type>(duration)

Example: converts 1 tick of 60 seconds to 60 ticks of 1 second.

std::chrono::minutes min1(1);
cout << min1.count() << endl; // 1
auto sec60 = std::chrono::duration_cast<std::chrono::seconds>(min1);
cout << sec60.count() << endl; // 60

Negative durations

Durations can be negative. There’s a abs() overload, so a negative duration can be convert either as is or when getting .count():

std::chrono::minutes min1(-1);
cout << min1.count() << endl; // -1
cout << abs(min1).count() << endl; // 1
cout << abs(min1.count()) << endl; // 1

Time Point

Structure: std::chrono::time_point<Clock>. Clock is the underlying mechanism for measuring time, most of times we use std::chrono::system_clock.

A duration can be obtaining by the different between two time points.

Get current Unix timestamp

#include <chrono>
auto timestamp = std::chrono::system_clock::now();
auto unixtime = std::chrono::duration_cast<std::chrono::seconds>(
  timestamp.time_since_epoch()
).count()

Modules

Header Files

Avoid importing the same file multiple times:

#pragma once

Cleaner and less error-prone than using the triad #ifndef/#define/#endif.

Concurrency

See Concurrency.

Functional

Lambda

auto is_less_than = [](auto a, auto b) {
    return a < b;
};
is_less_than(3, 3.4); // true

With bindings:

int target = 5;
auto is_less_than_target = [target](auto a) {
    return a < target;
};
is_less_than_target(3); // true

With references bindings:

int target = 5;
auto dec = [&target]() {
    target--;
};
dec();
target; // 4

When values are captured via copy they’re const. To change it use mutable:

int target = 5;
auto dec = [target]() mutable {
    target--;
    std::cout << target << std::endl;
};
dec();

Return types are inferred but they can be provided explicitly:

int target = 5;
auto dec = [target]() -> int {
    return target - 1;
};
std::cout << dec() << std::endl;

Passing Function As Parameter

Lambda:

std::vector<int> map(std::vector<int> v, std::function<int(int)> f) {
  std::vector<int> v2;
  for(auto e:v) {
    v2.push_back(f(e));
  }
  return v2;
}

std::vector<int> vec = {1, 2, 3};
auto f = [](int x) { return 2 * x; };
auto v2 = map(vec, f); // v2 = {2, 4, 6}

Class method:

struct Caller {
  std::function<int(int)> cb_;
  Caller(std::function<int(int)> cb) : cb_(cb) {}

  int call(int x) {
    return cb_(x);
  }
};

struct K {
  Caller c_;
  K() : c_(std::bind(&K::f, this, std::placeholders::_1)) {}

  int f(int x) {
    return x + 1;
  }
};

auto obj = K();
cout << obj.c_.call(3) << endl;

Namespaces

Empty Namespace

Keywords: anonymous, unnamed

This makes x local to the file and doesn’t risk being exposed if some other file includes it.

namespace {
  int x;
}

Nested namespace

namespace n1::n2::n3 {
  int x;
}

Which is equivalent to

namespace n1 { namespace n2 { namespace n3 {
  int x;
}}}

Namespace aliasing

namespace my_alias = some::other_namespace;

Namespace resolution

Suppose we’re in namespace a::b, and inside it we refer to a variable x via c::x. What namespace does the compiler search in?

Once if has a namespace, use the x defined there. Report error if not. The key insight is that it binds to a namespace before searching for symbols there, so this case:

namespace a::b::c { }
namespace a::c { int x = 1; }
namespace a::b {
  int y = x;
}

Will lead to a compilation error since it binds to a::b::c first, even though x is defined in a::c. If we comment the first line it works.