C++ Snippets

A collection of code snippets, general notes, small pearls of wisdom and bits of knowledge, that may come handy at times.

Don’t expect well thought out descriptions or highly structured content here; these are snippets. (But there are some links spread around for further reading.)


Is a C++ compiler being used?

Check for the predefined macro __cplusplus (two leading underscores!).
It’s defined as an integer literal value when the translation unit is compiled as C++.
When expanded, it denotes the version of the C++ standard that is being used; the currently valid values are:

Usage examples:

  1. Bail out if no C++ compiler is used:

    #if !defined (__cplusplus)
    #error C++ compiler required
    #endif
    
  2. Enable features based on the standard version:

    #if __cplusplus >= 201402L
        // ...
    #elif defined(...) || defined(...)
        // ...
    #else
        //...
    #endif
    
  3. Enclose a code block in extern “C” {…}
    (That makes function names in C++ have C linkage (meaning the compiler won’t mangle the name), so that client C code can use the functions by using a C compatible header file that contains just the declaration of the function):

    #ifdef __cplusplus
    extern "C" {
    #endif
    // ...
    #ifdef __cplusplus
    }  /* end of the 'extern "C"' block */
    #endif
    

Common Containers

Overview of the most common/usuable/popular container types and some hints on how to choose an apt one.

Further reading:

Sequence containers

Name #include Is size fixed? Will size vary wildly? Is order important? Insert/erase at front/back? Insert/erase in the middle? Need to find nth element? Need to merge collections?
std::array <array> X - - - - X -
std::vector <vector> - - - - - X -
std::list <list> - X - X X - X
std::deque <deque> - X - X - X -
std::stack <stack> - X X X - - -
std::queue <queue> - X X X - - -

A few question you should ask yourself:

Associative containers

Name #include Has Key/Value association? Value is key? Is key unique? Is ordered by key? Hashable keys?
std::map <map> X - X X -
std::multimap <multimap> X - - X -
std::unordered_map <unordered_map> X - X - X
std::unordered_multimap <unordered_multimap> X - - - X
std::set <set> - X X X -
std::multiset <multiset> - X - X -
std::unordered_set <unordered_set> - X X - X
std::unordered_multiset <unordered_multiset> - X - - X

A few tips:


A Singleton Class

Useful for an object that should/must only exist once in the program (e.g. some random number generator, or some kind of “manager” object):

class Singleton
{
    public:
        Singleton(Singleton const&)            = delete; // Disable copy constructor.
        Singleton(Singleton&&)                 = delete; // Disable move constructor.
        Singleton& operator=(Singleton const&) = delete; // Disable copy assignment operator.
        Singleton& operator=(Singleton&&)      = delete; // Disable move assignment operator.
                                                         // [i] Marking functions as deleted is a C++11 feature.
        
        static Singleton& get ()
        {
            static Singleton instance; // (1) -> see info box -->
            return instance;
        }
        
        static int Value () { return get().ValueImpl(); }
    
    private:
        Singleton() {} // Hide constructor.
        
        int ValueImpl () { return some_value; }
        
        int some_value = 2;
};

And then use it in the other places like this:

#include "Singleton.hpp"

int main ()
{
    int x = Singleton::Value();
}

Source: Based on the video “SINGLETONS in C++” by The Cherno.


Identifier (names) reserved by the implementation

In C++, the implementation reserves:

ISO/IEC 14882:1998 (E) Programming languages - C++
17.4.3.1.2 Global names

  1. Certain sets of names and function signatures are always reserved to the implementation:
    • Each name that contains a double underscore (__) or begins with an underscore followed by an uppercase letter (2.11) is reserved to the implementation for any use.
    • Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.
      {Such names are also reserved in namespace ::std (17.4.3.1).}
Thus, my personal conclusion and advice:
Although using an underscore as the first character of an identifier is valid in certain contexts, it is much simpler to stay on track by avoiding a leading underscores at all (instead of trying to remember the exact rules and exceptions).

On binding of const and pointers

Hint: const applies to what is on its left; and if there’s nothing more on the left, it applies to the token on the right.

const char * A;
char const * B;
Both declare a pointer to a constant character — the value being pointed at will not change.
char * const C;
Declares a constant pointer to a character — the location stored in the pointer will not change.
const char * const D;
Declares a constant pointer to a constant character — neither the stored location (address), nor the value being pointed at will change.

From my own “Coding Style & Design Guide” page: Prefer the east const style:
Read from right to left [←] to see how it works (you can confirm it with the comment next to it):

int const n;         // n is a constant integer
int* p1;             // p1 is a (mutable) pointer to a (mutable) integer
int* const p2;       // p2 is a constant pointer to a (mutable) integer
int const* p3;       // p3 is a (mutable) pointer to a constant integer
int const* const p4; // p4 is a constant pointer to a constant integer

C++ Keywords

ISO C++ 20 keywords (without those that are reserved by a Technical Specification):

alignas alignof and and_eq asm auto
bitand bitor bool break
case catch char
char8_t char16_t char32_t class compl
concept const consteval constexpr constinit const_cast continue co_await co_return co_yield
decltype default delete do double dynamic_cast
else enum explicit
export extern
false float for friend
goto
if inline int
long
mutable
namespace new noexcept not not_eq nullptr
operator or or_eq
private protected public
register reinterpret_cast requires return
short signed sizeof static static_assert static_cast struct switch
template this thread_local throw true try typedef typeid typename
union unsigned using
virtual void volatile
wchar_t while
xor xor_eq

In addition to keywords, there are identifiers with special meaning, which may be used as names of objects or functions, but have special meaning in certain contexts:

final
import
module
override

The following tokens are recognized by the preprocessor when in context of a preprocessor directive:

if elif else endif
ifdef ifndef define undef
include line error pragma
defined __has_include __has_cpp_attribute
export import module

—Source for all: https://en.cppreference.com/w/cpp/keyword


Operator Precedence and Associativity of C++

The precedence rules of a language specify which operator is evaluated first when two operators with different precedence are adjacent in an expression.
Adjacent operators are seperated by a single operand. The precedence of operators is not directly specified, but it can be derived from the syntax.

The associatity rules of a language specify which operater is evaluated first when two operators with the same precedence are adjacent in an expression.

Operators in the same segment (on the same level) have equal precedence and are evaluated left to right, unless explicitly forced by parantheses.

The following table is taken from https://en.cppreference.com/w/cpp/language/operator_precedence and is valid for C++20; there are some more details on that page:

Operators are listed top to bottom, in descending precedence:

Precedence Operator Description Associativity
1 :: Scope resolution Left-to-right
🡲
2 a++   a-- Suffix/postfix increment and decrement
type()   type{} Functional cast
a() Function call
a[] Subscript
.   -> Member access
3 ++a   --a Prefix increment and decrement Right-to-left
🡰
+a   -a Unary plus and minus
!   ~ Logical NOT and bitwise NOT
(type) C-style cast
*a Indirection (dereference)
&a Address-of
sizeof Size-of[note 1]
co_await await-expression(C++20)
new   new[] Dynamic memory allocation
delete   delete[] Dynamic memory deallocation
4 .*   ->* Pointer-to-member Left-to-right
🡲
5 a*b   a/b   a%b Multiplication, division, and remainder
6 a+b   a-b Addition and subtraction
7 <<   >> Bitwise left shift and right shift
8 <=> Three-way comparison operator(since C++20)
9 <   <=   >   >= For relational operators < and ≤ and > and ≥ respectively
10 ==   != For equality operators = and ≠ respectively
11 a&b Bitwise AND
12 ^ Bitwise XOR (exclusive or)
13 | Bitwise OR (inclusive or)
14 && Logical AND
15 || Logical OR
16 a?b:c Ternary conditional[note 2] Right-to-left
🡰
throw throw operator
co_yield yield-expression(C++20)
= Direct assignment (provided by default for C++ classes)
+=   -= Compound assignment by sum and difference
*=   /=   %= Compound assignment by product, quotient, and remainder
<<=   >>= Compound assignment by bitwise left shift and right shift
&=   ^=   |= Compound assignment by bitwise AND, XOR, and OR
17 , Comma Left-to-right
🡲

Additional notes for the operator section

A few grammar rules cannot be expressed in terms of precedence (also known as binding strength) and associatity.
For example, a = b &lt; c ? d=e : f=g means (a = b &lt; c) ? (d=e) : (f=g), but you have to look at the grammar to determine that.
The C++ Programming Language, 3rd Ed.; 6.2 Operator Summary


Implicit conversion

A constructor that takes exactly one argument serves two purposes:

To prevent this from happening, use the explicit keyword.

Source: The Old New Thing by Raymond Chen: “Beware the C++ implicit conversion” (with an example).


Initialization values

If an initializer is specified for an object, that initializer determines the initial value of an object.

Source: Bjarne Stroustrup, The C++ Programming Language (3rd Ed.), §4.9.5.


References and pointers

From a blog entry by Len Holgate:

A pointer can either point to something, or not, whereas a reference must always refer to something. If you see a pointer in a piece of code you often have to look around a bit to find out if the pointer is valid or if it could be null. If you see a reference you know straight away that it is valid.

That’s pretty much all there is to it. If the code you are writing uses an object that is optional then you might choose to use a pointer to represent that optionality, if the object is not optional and must always be present then you should use a reference.


Using parenthesis with a new statement1

An answer from Brandon Bray (working on the Visual C++ Compiler team) in a forum’s posting:

That’s not entirely true. You’re all correct that putting empty parenthesis after the type in local declaration can be confused with a function prototype, the new expression is very different.

In general, if the () are included in a new expression (i.e. “new T()”), then the item is value intialized (basically the compiler zeros out the memory first).
If the parenthesis are omitted, and the type is a non-POD, item is default initialized.
If the item is POD type, then state of the program is indeterminate (and in some cases the program is ill-formed).

So, there’s a performance cost to including the () in a new expression, but it makes the program more likely to be correct.

More info is in the C++ Standard 5.3.4/15. […]


Sizes of built-in types

C++ guarantees that a char is exactly one byte which is at least 8 bits, short is at least 16 bits, int is at least 16 bits, and long is at least 32 bits.
It also guarantees the unsigned version of each of these is the same size as the original, for example, sizeof(unsigned short) == sizeof(short).

1 ≡ sizeof(char) ≤ sizeof(short) ≤ sizeof(int) ≤ sizeof(long)

1 ≤ sizeof(bool) ≤ sizeof(long)

sizeof(char) ≤ sizeof(wchar_t) ≤ sizeof(long)
sizeof(float) ≤ sizeof(double) ≤ sizeof(long double)

sizeof(N) ≡ sizeof(signed N) ≡ sizeof(unsigned N)
Where N is either char or short int or int or long int.

Multiple variables in a for-loop

Multiple variables in a for-loop are allowed, but only if they’re all of the same type, since the comma in a declaration separates declarators, it doesn’t end a declaration:

for (int i = 0, j = 0; ... ; ++i, ++j) { ... }

Static member variable

Static members need to be defined in the source file, or the linker won’t be able to find them.

Exception: Static, constant, integral members for which one doesn’t take the address.
Most compilers will allow one to initialize them in the class declaration and no corresponding definition is required.

The declaration of a static data member in the member list of a class is not a definition!
You must define the static member outside of the class declaration, in namespace scope:

class X
{
public:
      static int i;
};

int X::i = 0; // definition outside class declaration

Once you define a static data member, it exists even though no objects of the static data member’s class exist.
In the above example, no objects of class X exist even though the static data member X::i has been defined.

Source:


Type aliases with #define, typedef or using

There are multiple ways on one can employ an type alias (e.g. for shortening a long type expression):

#define

The oldest method is to use #define:

#define ULONG_ALIAS unsigned long

Another example:

#define INT_POINTER_ALIAS int *

And while this (poor) solution may be fine for a few simple cases, it can also soon became very messy:

Say you want to create an alias ARRAY_POINTER_ALIAS for a pointer to an array of ints.
You cannot simply write a #define for the name only, because there should be text both before and after the variable name; you have to use a parameter, like so:

#define ARRAY_POINTER_ALIAS(a) int (*a)[]

Which could become very cumbersome when you have to take into account multiple variable declarations, sizeofs, and even more complicated things, like further typedef or #define decalrations.

typedef

The next best thing for an alias declaration is using typedef:

typedef int* INT_POINTER_ALIAS;

typedef allows far more flexible use of the type system than #define. To come back to the case from before:
Say you want to create an alias ARRAY_POINTER_ALIAS for a pointer to an array of ints:

typedef int (*ARRAY_POINTER_ALIAS)[];

using

The latest and often recommended way is now to use an alias-declaration with using:

using ulong_alias = unsigned long;

Function Objects and Predicates1

Objects that have the operator() defined can be used as functions, and in most cases as replacements for functions. When the return value for an object’s definition of operator() is a bool (true or false), that object can be used as a predicate. That is, a predicate is a function that returns true or false.

In C++, a predicate is a special kind of function object. Function objects have a distinct advantage over traditional functions. Because function objects are first and foremost objects, they have all the advantages of objects — class hierarchies, methods, state variables, operators, and so on. They can be passed as objects and held in containers. The state variables of a function object can be accessed before and after they’re used with any container or algorithm.

C++ has a number of built-in function objects and predicates (see Standard Library).

Source: What Are Function Objects and Predicates? (2006).


Access Control: private, protected, public1

[TODO] friend? Note that the access control doesn’t hide anything (still visible)

Inheritance rules

Example:

class B                    { ...      };
class D_priv : private   B { ...      };
class D_prot : protected B { ...      };
class D_publ : public    B { ...      };
class UserClass            { B b; ... };

To make a public member of B so it is public in D_priv or D_prot, state the name of the member with a B:: prefix. For example, to make member B::f(int,float) public in D_prot, you would say:

class D_prot : protected B
{
	public:
		using B::f;
};

Sources:


static_assert() and assert()

Assertions are helpful features for testing/debugging and for noticing errors/bugs that should never happen.

An assertion doesn’t allow to recover from an error (which, after all, should never had happenend in the first place).

static_assert()

A keyword/declaration that checks a software assertion at compile-time.
If the specified constant expression is false, the compilation fails with an error and displays the specified message (if one is provided).

static_assert(bce, msg); // Available since C++11.
static_assert(bce);      // Since C++17, the message parameter is optional.

assert()

A function-like pre-processor macro that checks a software assertion at run-time.
Evaluates an expression and if the result is false, aborts/terminates the program (and usually prints a diagnostic message).

// #define NDEBUG // uncomment to disable assert()
#include <cassert>
assert(condition);

There is no standardized interface to add an additional message to assert errors, but there are workarounds:

assert((2 + 2 == 4, "Message")); // -> 1.
assert(condition && "Message");  // -> 2.
  1. A portable way is to use a comma operator (provided it has not been overloaded).
  2. Combining a string literal (which always evaluates to true) by logical AND with the condition doesn’t impact the evaluation of the assert.

std::variant

A variant is a data type/class template that can hold values of different types (much like a union in C) and is available since C++17.

At its core, std::variant is a union of types: It can store one value at a time from a predefined set of types.
Unlike a traditional union, std::variant brings type-safety to the table and keeps track of its active type, ensuring that one accesses the correct value type.

A variant can hold values of various data types, including fundamental types (int, double, etc.), user-defined types (custom classes or structs), and even other variants. This flexibility and versatility opens up a world of possibilities for handling complex data scenarios; e.g.:

At any given time, an instance of a variant either holds a value of one of its alternative types, or no value (this state is a bit harder to achieve, see std::monostate below for details).

#include <iostream>
#include <iomanip>
#include <format>
#include <variant>   // <-- !

std::variant<std::monostate, int, double, bool, std::string> val;
          // |               |    |       |     |
          // |               |    |       |     +- Index 4 }
          // |               |    |       +------- Index 3 } For comparison later
          // |               |    +--------------- Index 2 } with the result of
          // |               +-------------------- Index 1 } val.index()
          // +------------------------------------ Index 0 }


val = "String test value";

if      (std::holds_alternative<int>(val))         { std::cout << std::format("val is: {0}\n", std::get<int>(val)); }
else if (std::holds_alternative<std::string>(val)) { std::cout << std::format("val is: {0}\n", std::get<std::string>(val)); }
else if (std::holds_alternative<bool>(val))        { std::cout << "val is: " << std::boolalpha << std::get<bool>(val) << '\n'; }
else                                               { std::cout << "..." << '\n'; }


val = 305; // Same variant variable, different type.

auto r = std::get_if<std::string>(&val);

if (r) { std::cout << std::format("val is: {0}\n", *r); }
else   { std::cout << "val is: At least not a string this time...\n"; }


val = 1.5;

std::cout << std::format("Current type is the alternative with index {0}\n", val.index());
    // Which prints '2' for the index and which checks out (see above).

// 'val' still holds a 'double' value, but we now try to get an 'int' value without checking first;
// that that will raise an exception:

try                                      { std::get<int>(val); }
catch (const std::bad_variant_access& e) { std::cerr << "Error: " << e.what() << std::endl; }

It’s important to check which type is stored in a variant before retrieving the value (use std::holds_alternative or std::get_if for that); otherwise an exception might get raised/thrown (see example code above).

A few interesting/helpful functions

Function (Non)Member Description
index() Member Returns the zero-based index of the alternative held by the variant.
The index is related to the order of alternatives in the variant when it’s initially defined!
valueless_by_exception() Member Checks if the variant is in the invalid state; returns true or false
visit... Non-Member ??? Didn’t need (or understand) it yet… (Maybe see and this)
holds_alternative<T>(V) Non-Member Checks if a variant V currently holds a value of type T; returns true or false.
get<T>(V) Non-Member Retrieves the value of the given type (or index) T from the variant V; throws on error (std::bad_variant_access).
By the way: get returns a reference, so one also can change the value that way!
get_if<T>(V) Non-Member Returns a (null)pointer to the value of a pointed-to variant V, given the index (or type, if unique) T.

std::monostate

Tip: Always put a std::monostate as the first entry in a std::variant: Makes the variant default-constructible and makes it also easier to detect an empty state.

The original purpose of std::monostate was to be used as the initial type in a std::variant, to allow it to be default-constructed (which always uses the first alternative) in an empty state; in case it holds no alternative.

Imagine something like std::variant<A, B>: Maybe there exist no default constructors for type A or B, or they may have unwanted side effects.
What can you do with a monostate? Nothing! Its job is just to be a dummy type that you can use when you are forced to provide a default-constructible type but don’t want to.

In the case of a std::variant, you can think of inserting std::monostate as a way to add an “empty” state to a variant, saving you the trouble of having to create a std::optional<std::variant<...>>. You can treat the std::monostate as a dummy state, representing an “empty” or void state.
(Note: There is already a member funktion to check for an “empty” state, known as valueless_by_exception(), but this is “wacky”, so it’s better to avoid it as much as possible.)

Based on Raymond Chen’s What’s the point of std::monostate? You can’t do anything with it! (see also the related discussion thread on Reddit).

More on the topic


std::optional

The class template std::optional manages an optional contained value (i.e. a value that may or may not be present) and is available since C++17.

It lets you augment the values of a type T with an additional value of std::nullopt, which represents the absence of a value.
Therefore, a std::optional which holds the value std::nullopt is known as empty (which is also its orginal state when created).

#include <optional>

std::optional<int> var;
if (var == std::nullopt) { /* Do something */ }

The basic operations on std::optional are these (more…):

Function Description
has_value() Checking if it holds a value. Returns true or false.
value() Retrieving the value. Throws a std::bad_optional_access exception if the object is empty!
= Assigning a value.
reset() Clearing the value and returning to the empty state.

Other interesting features:

Contextual conversion

If used in places where the language expects a boolean (if, ||, etc.), a std::optional is considered true if is has any value and false if it is empty.
In short, if (var) is the same as if (var.has_value()).

Note that this does not test whether the wrapped value is true or false:

std::optional<bool> var1 = false;
if (opt1) { /* This executes because the variable is non-empty (even though it is false) */ }

std::optional<void*> var2 = nullptr;
if (opt2) { /* This executes because the variable is non-empty (even though it is nullptr) */ }

This might be confusing to the reader if the value type itself is a bool; in those cases, better be explicit and use if (var1.has_value()).

Equality comparison against a value

An empty std::optional<T> compares unequal to any T.

std::optional<int> var;
if (var == 0) {
    // Does NOT execute because the variable is currently empty
    // and is therefore not equal to any integer.
}

One can use this instead of the more verbose if (opt.has_value() && opt.value() == 0).

Ordering comparison against a value

An empty std::optional compares less than any non-empty std::optional, and also less than any value.

std::optional<int> var;
if (var > 0) { /* Does NOT execute, because 'empty' is less than all values. */ }

Better to avoid this, except when sorting, because this behavior differs from NaN (another popular “There’s nothing useful here” value) in that the corresponding opposite-sense test does execute:

if (opt <= 0) { /* Executes because 'empty' is always less than any value. */ }

An alternative (but not necessarily a good one, see below):

if (opt.has_value() && *opt > 0) // or...
if (opt.has_value() && *opt < 0)

But those also have a trip wire: Both opt.value() and *opt return the wrapped value, but have different failure modes:

In the above case, you can write the code equivalent as the following, because the compiler can optimize out the redundant emptiness test:

if (opt.has_value() && opt.value() > 0) // or...
if (opt.has_value() && opt.value() < 0)

🡲 Based on: The Old New Thing: Some lesser-known powers of std::optional


Read binary data from a file to a std::vector buffer (and write back)

#include <iostream>
#include <fstream>

std::ifstream infile("C:\\some\\path\\in.dat", std::ios::binary);
infile.seekg (0, infile.end);         // Jump to the end of the file.
size_t file_length = infile.tellg();  // Note down that last position as the length.
infile.seekg (0, infile.beg);         // Set the read/get pointer back to the beginning.

// The buffer for the file's data.
std::vector<unsigned char> file_data;
file_data.resize(file_length);

// Read all data from the input file into the buffer:
infile.read((char*)file_data.data(), file_data.size());
infile.close();

// Write the data from the buffer to an output file:
std::ofstream outfile;
outfile.open("C:\\some\\path\\out.dat", std::ios::binary);
outfile.write(reinterpret_cast<const char*>(&file_data[0]), file_data.size());
outfile.close();

std::cout << "Input file size: "         << file_length      << " bytes\n";
std::cout << "File data (buffer) size: " << file_data.size() << " bytes\n";

Exceptions

#include <stdexcept>

try
{
    int i = 11;
    
    if (i > 10)
        throw std::invalid_argument("an invalid value");

    if (i < 5)
        throw i;
}
catch (const CustomException& e)       { std::cerr << "Error: " << e.what() << '\n'; }
catch (const int& e)                   { std::cerr << "Error: The number too low: " << e << '\n'; }
catch (const std::invalid_argument& e) { std::cerr << "Error: " << e.what() << '\n'; }
catch (const std::range_error& e)      { std::cerr << "Error: " << e.what() << '\n'; }
catch (...)                            { std::cerr << "Error: Unknown exception!\n"; }
    // ^^^ Handle any other exception. But here we don't have any details about the exception.

Rethrow exception

By simply using a plain throw, the currently handled exception will be re-thrown (up the stack), as is.

But one can also add information before re-throwing or pick data from the current exception and then throw another exception type.

Tip: Always catch by const reference if you want to re-throw an exception; otherwise the exception may get sliced or become invalid, or… or…

try { /*...*/ }
catch (const std::invalid_argument& e)
{
    // 1. Do something here...
    // 2. Then let someone higher up the call stack handle the rest...
    throw;
}
catch (const int& e)
{
    throw runtime_error("Something weird happend in function() when the value was " + string(e));
}

If an exception bubbled up to the very top and was never caught, then only the termination of the program is guaranteed (by the standard), most of the process is optional/implementation-specific.

Even the unwinding of the stack (invoking destructors and cleaning up other stuff) will normally not happen (also because that would destroy data that could help with troubleshooting or debugging); the same goes for displaying a message to the user: Depends on the runtime/operating system.

Tip: Use an enclosing try/catch (all) clause to be on the safe side, like this in main():

int main ()
{
    try
    {
        try
        {
            /* ... */
        }
        catch (const int& e)
        {
            throw std::runtime_error("Something weird happend in function() when the value was " + e);
        }
        
        return 0;
    }
    catch (const std::exception& e)
    {
        std::cout << "Uncaught exception that bubbled up to the top: " << e.what() << '\n';
        return 1;
    }
    catch (...)
    {
        std::cout << "Uncaught exception that bubbled up to the top!\n";
        return 1;
    }
}

Custom exception

class CustomException : public exception
{
    public:
        CustomException(const string& msg) : message(msg)
        {
            // ...
        }

        ~CustomException()
        {
            // ...
        }

        virtual const char* what() const throw () 
        {
            return message.c_str();
        }

    private:
        string const message;
};

Remove any white-space characters from a std::string

#include <iostream>
#include <algorithm>

std::string s = "A string with spaces"
s.erase(remove_if(s.begin(), s.end(), isspace), s.end());

std::cout << s << '\n';

Copy (or rather: concatenate) a std::string to a char array

std::string source
char destination[MAX_PATH] = { '\0' };

strncat(destination, source.c_str(), MAX_PATH - strlen(destination));

Measure time duration

auto start = std::chrono::high_resolution_clock::now();
// [The code that should be measured/profiled...]
auto end   = std::chrono::high_resolution_clock::now();

std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();

Namespace

👉 Has its own page.


PImpl (Pointer to Implementation)

Which is also known as:

Using the PImpl idiom has a few pros and cons:

Advantages:

Disadvantage:

Example

Inspired by a YouTube video from Mike Shah.

Header file (Person.hpp)

The header file only needs a forward declaration and a pointer to that implementation class (which is an incomplete type at this moment, see next paragraph), instead of the actual members. That way, it’s left to the implementation how the class actually stores its data and details about it are hidden.

Important: A destructor must be declared (in the header) and defined (in the implementation), even when using a smart pointer and nothing else happens in the implementation (could be defaulted: Person::~Person() = default;)! Otherwise a compilation error will occur, because the PImpl class is still an incomplete type.

#include <string>
#include <memory> // for std::unique_ptr

class Person
{
    // Using PImpl                    // Not using PImpl
    // -------------------------------//---------------------
    public:                           //
        Person (std::string s);       //
        ~Person ();                   //
        std::string GetAttributes (); //
                                      //
    private:                          //
        class PImpl;                  // std::string name;
                                      // std::string height;
        PImpl* pimpl;                 //
        // ... or ...                 //
        std::unique_ptr<PImpl> pimpl; //
};                                    //

Implementation file (Person.cpp)

Since the class Person::PImpl is only defined in the implementation file, one can now change the implementation of the class without affecting the ABI or API (communicated by the header).

#include "Person.hpp"

class Person::PImpl
{
    std::string name;
    std::string height;
};

Person::Person (std::string s)
{
    // Using PImpl                             // Not using PImpl
    // ----------------------------------------//---------------------
    pimpl = new PImpl;                         //
    // ... or ...                              //
    pimpl = std::make_unique<PImpl>();         //
                                               //
    pimpl->name   = s;                         // name(s);
    pimpl->height = "not set";                 // height = "not set";
}

Person::~Person ()
{
    delete pimpl; // Only needed for a raw pointer, not for a unique_ptr!
}

Person::GetAttributes ()
{
    // Using PImpl                             // Not using PImpl
    // ----------------------------------------//---------------------
    return pimpl->name + ", " + pimpl->height; // return name + ", " + height;
}

Program file (main.cpp)

#include <iostream>
#include "Person.hpp"

int main ()
{
    Person p("Sascha");
    std::cout << p.GetAttributes() << std::endl;
    return 0;
}

Recommendable resources

Places on the net

Books1


  1. Copied from my old “C and C++ notes” HTML file from around 2005/2006; might be outdated/may need an update! ↩︎ ↩︎ ↩︎ ↩︎