logo

Modern C++

Last Updated: 2022-02-08

Modern C++ = at least C++11 (i.e. C++11, C++14, C++17, C++20 and C++23).

  • C++20 big new features: modules, ranges, coroutines, concepts
  • C++17: added and removed many features, but not as big as C++11 or C++20.
  • C++14: a small extension over C++11, with bug fixes and small improvements.
  • C++11 big new features: smart pointers, lambda expressions, rvalue references

Check if a feature is supported in clang: https://clang.llvm.org/cxx_status.html

Smart Pointers

noexcept

Compile-time check, specifies that the function will NOT throw any exceptions.

Type inference: auto

Range-based for loop

Similar to Java and Python:

for (int& x : my_array)
  x *= 2;

Lambda Function

[](int x, int y) -> int { return x + y; }

Polymorphic lambda: in the following example, type of x depends on type of n

double n = 1.0; // or int n = 1;
auto fp = [] (const auto &n) -> auto {return n*2;};
auto x = fp(n);

Function Object (Functors)

An object that has an operator() defined; it can be called with function call syntax.

  • std::function (since C++11) requires that the callable it wraps be CopyConstructible
  • std::move_only_function (since C++23) can store and invoke any constructible (not required to be move constructible)

At its core, std::function is a type-erasing container, which may be used to hold any functor.

Functions are value types; no heap allocation is required.

RAII: Resource Acquisition Is Initialization

A common C++ pattern. It couples the use of a resource to the lifetime of an object:

  • construction of the object <=> acquire the resource
  • destruction of the object <=> release the resource.

This is similar to try-with-resources in Java

C++14 type traits

std::is_signed<T>::value;     /* ---> */   std::is_signed_v<T>;
std::remove_const<T>::type;   /* ---> */   std::remove_const_t<T>;

Modules

Since C++20.

Traditionally, when a C++ library X wants to expose its interface to another library Y, X provides a set of declarations in the form of a .h file, which is textually copied into Y’s source files by the preprocessor.

problems:

  • The compiler cannot restrict visibility of symbols.
  • Textual inclusion is brittle. the point and order of inclusion affects the meaning of the program.
  • Textual inclusion does not scale. without ongoing cleanup efforts, textual inclusion leads to a superlinear growth in average C++ parse times.

Modules:

  • Will replace header files in the long run;
  • explicit about what is exported
  • the same module is only processed once, so it will improve build throughput (versus header file will be copied into each source file)

The C++20 standard includes a specification for modules (standard modules). Clang has a different realization of modules (Clang modules, or header modules).

Ranges

Since C++20.

Similar to begin()/end() iterator pairs; can directly sort(data);

Coroutine

Since C++20.

A function with one of the following: co_await, co_yield, co_return

  • co_return: "return and don’t call me again"
  • co_yield: "return and resume here on the next call."

A coroutine is simply a function that behaves differently when called multiple times: Instead of restarting from the start on each call, it will continue running after the return statement that last returned. To do this, it needs some state to store the information of where it last returned, and what the state of the local variables was at that point. In existing usage that meant that you need a program stack to store this state, but in C++ this is just stored in a struct.

Designated initializers

Since C++20

struct Point {
  double x;
  double y;
  double z;
};

Point point = {
    .x = 3.0,
    .y = 4.0,
    .z = 5.0,
};

std::vector<Point> points;
points.push_back(Point{.x = 3.0, .y = 3.0});