C++ Cheatsheet

kuniga.me > Docs > C++ Cheatsheet

C++ Cheatsheet

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

Index

  1. Basic Types
    1. Array
      1. Array vs std::array
    2. Conversions
      1. int to string
    3. Enum
    4. Optional
    5. Strings
      1. Literal
      2. View
    6. Sstream
      1. Osstream
    7. Struct
  2. Collections
    1. Hash Map
      1. Import
      2. Initialization
      3. Access
      4. Insert
      5. Iterating
      6. Search
    2. Set
      1. Initialization
      2. Remove
    3. Vector
      1. Initialization
      2. Iterate
      3. Empty
      4. Size
      5. Slice
  3. Memory
    1. Shared Pointers
  4. Flow Control
    1. Exceptions
  5. Object-Oriented
    1. Class
      1. Constructor
      2. Destructor
      3. Instantiate
      4. Methods
    2. Inheritance
      1. Visibility
      2. Abstract methods
      3. Interface
      4. Override methods
  6. Template
    1. Function
    2. Class
    3. Inner Type
    4. Specialization
      1. Type alias
      2. Implementation
      3. Variadic
  7. Compile Time
    1. Static Assert
    2. Constant Expressions
      1. Syntax
    3. Type Traits
      1. Type Equality (=)
      2. Conjunction (&&)
  8. Files
    1. Check if file exists
    2. Create directory
    3. Create temporary file name
    4. Read from file
    5. Write to file
  9. Modules
    1. Header Files
  10. Functional
    1. Lambda
    2. Passing Function As Parameter

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

enum class MyBoolean { kYes, kNo };

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;
}

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

[C++17] 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};

Collections

Hash Map

In C++ unordered_map implements a hash map. Search, insertion, and removal of elements have average constant-time complexity.

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;

Insert

h["key"] = 100;

Iterating

x is a pair (key, value).

for (auto x:h) {
    std::cout << x.first << ", " << x.second << std::endl;
}
bool has_key = h.find("key") != h.end();

Set

Initialization

From literals:

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

Remove

Remove entry (in-place):

s.erase(value);

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());

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;

Flow Control

Exceptions

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

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

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.

Template

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::end;
}

Modules

Header Files

Avoid importing the same file multiple times:

#pragma once

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

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

By default the reference binding is const. To change it use mutable:

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;