Frequently Used Modern C++ FeaturesΒΆ

Serac currently uses C++17. Several modern C++ features and library components are used heavily throughout Serac.

Smart pointers are used to avoid directly using operator new and operator delete except when absolutely necessary. std::unique_ptr<T> is used to denote exclusive ownership of a pointer to T - see this article for more info. Because unique_ptr implies unique/exclusive ownership, instances cannot be copied. For example, if a function has a unique_ptr argument, a caller must utilize move semantics to transfer ownership at the call site. The linked article provides an example of this, and move semantics are discussed in a more general sense here.

std::shared_ptr<T> is used to denote shared ownership of a pointer to T - see this article for example uses. shared_ptr s should be used sparingly. Often, when two objects need to share a resource, it is sufficient for only one of the objects to be responsible for the lifetime of the shared resource; the other object can store a reference to the resource.

std::optional<T> is used to express the idea of Maybe T, a.k.a. a nullable type. An optional is optionally a T, which is useful as a return type for functions that can fail. It is preferable to values that are implied to be invalid or represent failure, e.g., std::optional<int> should be used instead of -1 to represent an invalid array index. It is also preferred as an alternative to functions that return nullptr on failure. You can read more about optional here.

std::variant<T1, T2, T3, ...> is use to express the idea of Either T1 or T2 or T3 or .... It is the type- and memory-safe version of a union. This article goes into more detail, but typically this is used to "tie" together classes that are used in the same context but are not conducive to an inheritance hierarchy.

Lambdas are also used frequently to declare small functions immediately before they are used, e.g., before they are passed to another function. Lambdas are very useful with std::algorithm s (introduced here), which are often preferable to traditional for loops as they more clearly express intent. Lambdas can also capture variables available in the scope in which they are declared - see this page for more info.

Finally, range-based for loops (described here) should be used whenever possible instead of integer-iterator-based indexing. This is supported for all standard library containers.

For a comprehensive overview of modern C++ (C++11 onwards), Scott Meyer's Effective Modern C++ is quite useful.