kuniga.me > Docs > C++ Cheatsheet
Syntax for common tasks I run into often. Assumes C++17.
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;
std::array()
has friendlier semantics (discussion).
#include <string>
std::string = to_string(10);
Strong enumeration, scoped enumeration
enum class MyBoolean { kYes, kNo };
Unscoped enumeration (without class
modifier):
enum MyBoolean { kYes, kNo };
(scoped enum)
MyBoolean b = MyBoolean::kYes;
(scoped enum)
MyBoolean b = MyBoolean::kYes;
cout << static_cast<bool>(my_enum) << endl;
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;
}
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();
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";
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
string_view::substr()
does not copy either.Construct streams using the <<
syntax.
#include <sstream>
#include <iostream>
std::ostringstream ss;
ss << "hello";
ss << " world";
std::cout << ss.str() << std::endl;
struct MyStruct {
int x;
double z;
}
Initialization:
MyStruct my_struct = {.x = 1, .z = 0.5};
using MyNewType = std::vector<std::string>;
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.
#include <unordered_map>
Empty map:
std::unordered_map<std::string, int> h;
std::unordered_map<std::string, int> m = { {'a', 1}, {'b', 2} };
std::cout << h["key"] << std::endl;
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.
h["key"] = 100;
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();
Prefer std::unordered_set
over std::set
.
From literals:
std::unordered_set<int> s = {4, 16, 9, 25};
Returns 1 if element
exists, 0 otherwise. Faster than .find()
.
s.count(element);
Remove entry (in-place):
s.erase(value);
Include:
#include <tuple>
std::tuple<int, std::string> t = std::make_tuple(1, "hello");
or:
auto t = std::make_tuple<int, std::string>(1, "hello");
std::get<n>
. Example:
std::cout << std::get<0>(t); // 1
std::cout << std::get<1>(t); // "hello"
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);
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
);
std::vector<int> vec{10, 20, 30};
for(auto e:vec) {
std::cout << e << std::endl;
}
vec.empty();
v.size();
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());
void f(std::vector<int> v) {}
// Type deduction
f({1, 2});
// Explicit type
f(std::vector<int> {1, 2});
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;
Allows pre-allocating into a larger chunk of memory into a buffer and then new
ing 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.
Initialize and evaluate inline:
if (bool x = false; !c) {
cout << "prints" << endl;
}
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;
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) {}
// Point.hpp
class Point {
...
public:
~Point();
};
// Point.cpp
Point::~Point(void) {
// Clean up allocated memory
}
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);
Read-only method. Add const after function signature.
struct C {
void my_method() const {
// Cannot modify x ()
}
int x;
};
Shared across instances.
struct C {
static int x;
};
// Definition and initialization must be done outside the class
int C::x = 0;
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() {
}
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 {};
If you’re coming from Java, C++ version of abstract methods are pure virtual methods.
class Base {
virtual void abstract() = 0;
};
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.
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 <typename T>
T id(T x) {
return x;
}
template <typename T>
struct Wrapper {
T v_;
public:
Wrapper(T v) {
v_ = v;
}
T get() {
return v_;
}
};
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();
}
template <typename T>
struct Generic {
T v_;
};
using Int = Generic<int>;
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);
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;
Syntax:
static_assert(<expr>, "message");
Where <expr>
must be a constant expression.
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;
};
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>;
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
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
Works for directories too.
#include <filesystem>
namespace fs = std::filesystem;
bool file_exists = fs::exists("path");
#include <filesystem>
namespace fs = std::filesystem;
fs::create_directory("path");
std::string filename = std::tmpnam(nullptr);
#include <fstream>
std::ifstream my_file("filename");
if (my_file.is_open()) {
while (getline(my_file, line)) {
std::cout << line << std::end;
}
}
#include <fstream>
std::ofstream my_file("filename");
if (my_file.is_open()) {
my_file << "a line" << std::endl;
}
my_file.close();
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
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()
Avoid importing the same file multiple times:
#pragma once
Cleaner and less error-prone than using the triad #ifndef/#define/#endif
.
Useful to avoid dependencies when the size of the class doesn’t need to be known in the header file. Example:
// my_header.h
#include "C.h"
void f(C* c);
we can get rid of #include "C.h"
via:
// my_header.h
class C;
void f(C* c);
Namespaced class. If the class being forwarded is within a namespace, we must forward-declare it within a namespace. Example:
// my_header.h
namespace c_ns {
class C;
}
void f(c_ns::C* c);
unique_ptr. It’s not possible to use forward declaration with std::unique_ptr
because it relies on the destructor function.
See Concurrency.
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;
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;
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;
}
namespace n1::n2::n3 {
int x;
}
Which is equivalent to
namespace n1 { namespace n2 { namespace n3 {
int x;
}}}
namespace my_alias = some::other_namespace;
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?
a::b::c
.a::c
.c
.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.
Inside a function, the type of a variable is always lvalue reference, even if declared as rvalue. Example:
struct C {};
void g(C& c) {
std::cout << "lvalue reference\n";
}
void g(C&& c) {
std::cout << "rvalue reference\n";
}
void f(C&& c) {
g(c); // lvalue ref
}
To keep this as rvalue, we need to do g(std::move(c))
.
Universal references are those with rvalue reference syntax but where type deduction happens, either via auto
or templates. The deduced type is either lvalue or rvalue, depending how it was called. Example:
struct C {};
template <typename T>
void f(T&& x) {
if constexpr (std::is_lvalue_reference_v<T>) {
std::cout << "lvalue reference\n";
} else {
std::cout << "rvalue reference\n";
}
}
C c;
f(c); // lvalue ref
f(std::move(c)); // rvalue ref
As with rvalue refs, inside f()
, x
is an lvalue reference, however, if we use std::move(x)
and T
was originally lvalue, we lose that info. If we want to preserve the reference type of x
, we need to use std::forward<T>(x)
(Item 25 on [1]).