Non-Virtual Polymorphism

Modern Features in C++17 Non-virtual runtime polymorphism can be achieved with modern C++ (e.g., C++17) features std::any and std::variant as described in the table below. Notice std::tuple is not used for polymorphism; it offers a structured way to manage multiple values of different types simultaneously, such as in function return types, or parameter packs. It is put here because of its usage is a bit similar to std::any and std::variant....

January 24, 2024 · 768 words · Me

Observable Behaviors

What is Observable Behavior & Related Issues The term observable behavior, according to the standard, means the following: — Accesses (reads and writes) to volatile objects occur strictly according to the semantics of the expressions in which they occur. In particular, they are not reordered with respect to other volatile accesses on the same thread. — At program termination, all data written into files shall be identical to one of the possible results that execution of the program according to the abstract semantics would have produced....

December 2, 2023 · 804 words · Me

Trivial Class vs Aggregate Structure

Trivial Class vs Aggregate Structure Trivial Class A trivial class is a class that: Has a trivial default constructor. Has a trivial copy constructor. Has a trivial move constructor (since C++11). Has a trivial copy assignment operator. Has a trivial move assignment operator (since C++11). Has a trivial destructor. Has no virtual functions or virtual base classes. The trivial constructors/operations/destructor means they are not user-provided (i.e., is implicitly-defined or defaulted on its first declaration)....

November 1, 2023 · 258 words · Me

Initialization With Brackets

The table summarizes how brackets {} and () are related to list-initialization in various contexts. The column Allows Narrowing Conversion indicates whether implicit type conversions that lose information are allowed. The column Allows Explicit Constructors indicates whether the syntax can call constructors marked as explicit. The columns Use for Aggregates and Use for User-Defined Types show the applicability of each initialization type for aggregates like arrays (e.g., int x[3][4]) and structs, and user-defined types like classes, respectively....

October 29, 2023 · 163 words · Me

Scope Guard

Background Scope Guard is a concept reminiscent of the RAII (Resource Acquisition Is Initialization) principle in C++. The idea is to manage resources (like memory, files, network sockets, etc.) using object lifetime. When the object goes out of scope, its destructor ensures that the resource is cleaned up properly. The scope guard is intended to run a given callable (like a function or lambda) when it is destroyed. RAII (Resource Acquisition Is Initialization) is a programming idiom used in C++ where the lifetime of an object is bound to the lifetime of its scope (typically represented by a block of code wrapped in curly braces {})....

August 29, 2023 · 629 words · Me

Static Local Member

C++ templates are blueprints and don’t represent specific types until they are instantiated with actual types. Once instantiated, the compiler creates a specific version of that template for the provided type. For template classes, each instantiation has its own unique version of the static members, making them distinct for each type the template is instantiated with. ///////////////////// // Code Block 1 ///////////////////// #include<iostream> class ComponentBase{ protected: // component_type_count is a static variable shared by derived classes static inline size_t component_type_count = 0; }; template<typename T> class Component : public ComponentBase{ public: static size_t component_type_id(){ // ID is the static local variable for a particular type T static size_t ID = component_type_count++; return ID; } }; class A : public Component<A> {}; class B : public Component<B> {}; class C : public Component<C> {}; int main() { std::cout << A::component_type_id() << std::endl; // 0 std::cout << B::component_type_id() << std::endl; // 1 std::cout << B::component_type_id() << std::endl; // 1 std::cout << A::component_type_id() << std::endl; // 0 std::cout << A::component_type_id() << std::endl; // 0 std::cout << C::component_type_id() << std::endl; // 2 } Key Points:...

August 27, 2023 · 373 words · Me

Formatter Specialization

We can customize the (printing) format of a given class by using the specialization of formatter. #include <format> #include <iostream> struct Frac { int a, b; }; template <> struct std::formatter<Frac> : std::formatter<string_view> { // parse() is inherited from the base class std::formatter<string_view> // * an efficient solution: auto format(const Frac& frac, std::format_context& ctx) const { return std::format_to(ctx.out(), "{}/{}", frac.a, frac.b); } // the same functionality as above, but inefficient due to the temporary string // auto format(const Frac& frac, std::format_context& ctx) const { // std::string temp; // std::format_to(std::back_inserter(temp), "{}/{}", // frac....

August 25, 2023 · 154 words · Me

User Defined Literals

User Defined Literals (UDL) produces an object in an interesting way: constexpr auto operator""_f(const char* fmt, size_t) { return[=]<typename... T>(T&&... Args) { return std::vformat(fmt, std::make_format_args(std::forward<T>(Args)...)); }; } auto s = "example {} see {}"_f("yep", 1.1); // s = "example yep 1.1" The UDL _f has the same effect of std::format("example {} see {}", "yep", 1.1). Pretty familiar (as libfmt), right? Now, let’s break the definition of _f down: int x = 10; double y = 3....

August 22, 2023 · 330 words · Me

Operator Overload

Reference: here. The return of overloaded operator should be a reference, otherwise return-by-code will create a (temporary) rvalue that cannot be passed to the next operation f2 by non-const reference. i.e., rvalue cannot be non-const referenced. #include <vector> #include <iostream> #include <functional> template<typename T, typename FN> requires std::invocable<FN, T&> // diff std::invocable? std::vector<T>& operator| (std::vector<T>& vec, FN fn) noexcept { for(auto& e: vec) { fn(e); } return vec; } int main(){ std::vector v{1, 2, 3}; auto f1 = [](int& i) {i *= i; }; std::function f2 {[](const int& i) {std::cout << i << ' '; } }; v | f1 | f2; }```

August 17, 2023 · 103 words · Me

Multidimensional Subscript Operator []

Finally, C++23 allows overload for the subscript operator [] to be multi-dimensional. Before that, we normally either use: vector of vector to form a matrix, and access it as mat[i][j] a class containing a big 1-d vector, but behaves as 2-d by overloading the operator (), e.g., mat(i,j) Now, with C++23, we advance the second option (which offers efficient memory access) with better indexing approaching as follow: template <typename T, size_t R, size_t C> struct matrix { T& operator[](size_t const r, size_t const c) noexcept { return data_[r * C + c]; } T const& operator[](size_t const r, size_t const c) const noexcept { return data_[r * C + c]; } static constexpr size_t Rows = R; static constexpr size_t Columns = C; private: std::array<T, R * C> data_; }; int main() { matrix<int, 3, 2> m; for(size_t i = 0; i < m....

May 13, 2023 · 198 words · Yac