Digital Garden
Computer Science
C++
STL - Standard Template Library

Standard Template Library - STL

Limits

The numeric_limits class template provides a standardized way to query various properties of arithmetic types.

cout << boolalpha;
cout << "Minimum value for int: " << numeric_limits<int>::min() << '\n';
cout << "Maximum value for int: " << numeric_limits<int>::max() << '\n';
cout << "int is signed: " << numeric_limits<int>::is_signed << '\n';
cout << "Non-sign bits in int: " << numeric_limits<int>::digits << '\n';
cout << "int has infinity: " << numeric_limits<int>::has_infinity << '\n';

Chrono

The chrono library is a flexible collection of types that track time with varying degrees of precision (system_clock, steady_clock, high_resolution_clock)

using Clock = chrono::system_clock;
Clock::time_pointstart = Clock::now();
Clock::durationd = Clock::now() - start;
int64_t ns = chrono::nanoseconds(d).count();
using ms_t = chrono::duration<double, milli>; // new durationtype
double ms = chrono::duration_cast<ms_t>(d).count();

Pair and Tuple

Pairs hold pairs of values as you would expect and tuples are like the mathematical tuples that are generalized pairs for any amount of values.

pair<int, double> id;
int i = id.first;
double d = id.second;
id = make_pair(3, 5.5);
 
tuple<int, double, string> tup(1, 2.2, "drei");
auto val= get<0>(tup); // read
get<1>(tup) = 1.5; // update
size_ts = tuple_size<decltype(tup)>::value; // number of elements
tup = make_tuple(2, 3.3, "vier");

Optional, Any and Variant

The class template optional manages an optional contained value, i.e. a value that may or may not be present which is a common use case when returning a value of a function that may fail.

optional<string> create(bool b) {
    if (b)
        return "Godzilla";
    return {}; // gleich wie nullopt;
}
auto opt = std::make_optional<std::vector<char>>({'a','b','c'});
if (opt) // or opt.has_value()
    cout << "value set to " << opt.value().value_or("nothing") << '\n'; // can also use function with or_else()

The class any describes a container for any single value.

any a = 1;
auto a0 = make_any<std::string>("Hello, std::any!\n");
cout << a.type().name() << ": " << any_cast<int>(a) << '\n';

the class template variant represents a type-safe union.

  variant<int, float> v, w;
v = 12;
int i = get<int>(v);
w = get<int>(v);
w = std::get<0>(v);
w = v;
// get<double>(v); error
// get<3>(v); error
try{
    get<float>(w);
}
catch (bad_variant_access&) {}

Container

A Container is an object used to store other objects and taking care of the management of the memory used by the objects it contains.

Attributes a ContainerX<T> needs to have:

  • value_type container-element, T.
  • reference reference of container-element.
  • const_reference same but read-only.
  • iterator the iterator for the container.
  • const_iterator same but read-only.
  • size_type

Functions a Container should have:

  • Standard-, copy and moveconstruktor, destructor
  • begin() and end() iterators, cbegin() and cend() for read only
  • max_size(), size(), empty()

Bitsets are a fixed-size sequence of N bits.

constexpr bitset<4> b1;
constexpr bitset<4> b2{0xA}; // == 0B1010
bitset<4> b3{"0011"}; // can't be constexpr yet
bitset<8> b4{"ABBA", /*length*/4, /*0:*/'A', /*1:*/'B'}; // == 0B0000'0110

Half dynamic structures like vector

vector<int> vec = {1,2,3};
cout << vec.size() << endl;
vec.push_back(5);
cout << vec[3] << endl;
vec[1] = 10;
cout << vec.front() << endl;
cout << vec.capacity() << endl;
vec.pop_back();
cout << vec.size() << endl;

List structures like normal linked lists forward_list, doubly linked lists list and double ended queues deque, also known as head-tail linked list.

std::deque<int> d = {7, 5, 16, 8};
d.push_front(13);
d.pop_back(25);
for(int n : d) {
    std::cout << n << ' ';
}

A set in C++ is a container that contains a sorted set of unique objects of type Key. Sorting is done using the key comparison function. Search, removal, and insertion operations have logarithmic complexity as they are usually implemented as red-black trees. multiset is the same except multiple keys with equivalent values are allowed.

set<string> mySet;
mySet.insert("first");
mySet.insert("second");
mySet.insert("third");
mySet.insert("first");
cout << "Set Size = " << mySet.size() << endl; // 3
mySet.erase("third");

A map is a sorted associative container that contains key-value pairs with unique keys. Multimap is the same except multiple keys with equivalent values are allowed.

map<string, int> m { {"CPU", 10}, {"GPU", 15}, {"RAM", 20}, };
for (const auto& [key, value] : m) {
    cout << '[' << key << "] = " << value << "; ";
}
m.erase("GPU");
m.insert("HDD", 50);

There are also unordered versions of sets and maps where the keys are not sorted: unordered_set, unordered_multiset, unordered_map, unordered_multimap

Iterators

Iterators in C++ do very similiar things to Iterators in Java. In C++ all they are are pointers. begin() / cbegin() return a pointer pointing to the first element and end() / cend() return a pointer that points to a fictional element after the last element.

template<class Iter> void print(Iter it, Iter end) {
    while(it!= end) {
        cout << *it++<< ' ';
    }
    cout << endl;
}

Move Iterator

A move iterator works exactly the same as a normal iterator except that dereferencing converts the value returned by the underlying iterator into an rvalue so it can be moved.

vector<string> source = { "Move", "iterators", "in", "C++" };
vector<string> destination(make_move_iterator(begin(source)), make_move_iterator(end(source)));

Insert Iterators

A front_insert_iterator prepends elements to a container for which it was constructed. The container's push_front() member function is called whenever the iterator (whether dereferenced or not) is assigned to. Similarly, there is the back_insert_iterator that appends to a container for which it was constructed.

vector<int> v{1,2,3,4,5};
deque<int> d;
copy(v.begin(), v.end(), front_insert_iterator<deque<int>>(d)); // or front_inserter(d)
for(int n : d)
    cout << n << ' ';
cout << '\n';

Reverse Iterator

reverse_iterator is an iterator adaptor that reverses the direction of a given iterator.

vector<int> v{1, 2, 3, 4, 5};
for (vector<int>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it)
{
    cout << *it; // prints 54321
}

Stream Iterator

copy(to_vector.begin(), to_vector.end(), std::ostream_iterator<int>(cout, " "));
copy_if(to_vector.begin(), to_vector.end(), ostream_iterator<int>(cout, " "), [](int x) { return x % 2 != 0; });

Algorithm

The algorithms in the algorithm header can be used on any container, no matter the implementation since they mainly use iterators. However, if there is a function with the same name on a container as in the algorithm header then that special version for the container should be used for better efficiency.

  • find<T>(begin(), end(), const T& value), find_if(begin(), end(), [](int i){ return i%2 == 0; }), find_if_not finds the first element satisfying specific criteria
  • nth_element(RandomIt first, RandomIt nth, RandomIt last) is a partial sorting algorithm that rearranges elements so that the element pointed at by nth is changed to whatever element would occur in that position if [first, last) were sorted. All of the elements before this new nth element are less than or equal to the elements after the new nth element. Can get median with: nth_element(v.begin(), v.begin() + v.size()/2, v.end());
  • ForwardIt1 search(ForwardIt1 first, ForwardIt1 last, ForwardIt2 s_first, ForwardIt2 s_last, BinaryPredicate p ) searches for the first occurrence of the sequence of elements [s_first, s_last) in the range [first, last). Elements are either compared using == or the optional binary predicate p. Returns an iterator to the beginning of the first occurrence of the sequence.
  • ForwardIt search_n( ForwardIt first, ForwardIt last, Size count, const T& value ); searches the range [first, last) for the first sequence of count identical elements, each equal to the given value. for example 5 consecutive zeros etc.
  • count, count_if returns the number of elements in the range [first, last) satisfying specific criteria.
  • T& min(const T& a, const T& b), max returns the smaller/bigger of the given values.
  • ForwardIt min_element( ForwardIt first, ForwardIt last ), max_element Finds the smallest/biggest element in the range [first, last).
  • bool lexicographical_compare( InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2 ) checks if the first range [first1, last1) is lexicographically less than the second range [first2, last2).
  • mismatch, equal mismatch finds the first position where two ranges differ, determines if two sets of elements are the same
  • OutputIt copy( InputIt first, InputIt last, OutputIt d_first ), copy_if copies the elements in the range, defined by [first, last), to another range beginning at d_first.
  • void swap( T& a, T& b ) exchanges the given values
  • void iter_swap( ForwardIt1 a, ForwardIt2 b ) swaps the values of the elements the given iterators are pointing to. Can implement a selection sort with it: void selection_sort(ForwardIt begin, ForwardIt end) {for (ForwardIt i = begin; i != end; ++i)iter_swap(i, std::min_element(i, end));}
  • void fill( ForwardIt first, ForwardIt last, const T& value ), fill_n( OutputIt first, Size count, const T& value ) assigns the given value to the elements in the range [first, last) (to first n elements).
  • void generate( ForwardIt first, ForwardIt last, Generator g ), generate_n assigns each element in range [first, last) a value generated by the given function object g.
  • void replace( ForwardIt first, ForwardIt last, const T& old_value, const T& new_value ), replace_if Replaces all elements that are equal to old_value.
  • replace_copy( InputIt first, InputIt last, OutputIt d_first,const T& old_value, const T& new_value ), replace_copy_if copies the elements from the range [first, last) to another range beginning at d_first replacing all elements satisfying specific criteria with new_value.
  • remove, remove_if, remove_copy, remove_copy_if also works just like replace.
  • transform( InputIt first1,InputIt last1, OutputIt d_first, UnaryOperation unary_op ) applies the given function to a range and stores the result in another range, keeping the original elements order and beginning at d_first.

Exceptions

Just like in Java some exceptions can be thrown and caught. It is best practice to only catch constant references of the exceptions. Unlike in Java, you don't explicitly say which exceptions a function will throw instead you define when a function does not throw any exceptions so that it can be better optimized.

try{
    throw runtime_error("example");
} catch(const runtime_error& e) {
    cout<< "std::runtime_error: " << e.what() << endl;
} catch(...) {
    cout<< "unknownexception" << endl;
}