Home » C++23: Syntactic Sugar with Deducing This

C++23: Syntactic Sugar with Deducing This

by admin
C++23: Syntactic Sugar with Deducing This

C++23: Syntactic Sugar with Deducing This

The Curiously Recurring Template pattern (CRTP) is a commonly used idiom in C++. It’s similarly hard to understand as the classic Viistor design pattern I introduced in my last article, “C++23: Deducing This Creates Explicit Pointers”. Thanks to Deducing This we can remove the C and R from the abbreviation.

Advertisement

Rainer Grimm has been working as a software architect, team leader and training manager for many years. He likes to write articles on the programming languages ​​C++, Python and Haskell, but also likes to speak frequently at specialist conferences. On his blog Modernes C++ he deals intensively with his passion for C++.

The acronym CRTP stands for the C++ idiom Curiously Recurring Tplate Pattern and designates a technique in C++ in which a class Derived is derived from a class template Base. The key point is that Base has Derived as a template argument.

template
class Base{

};

class Derived : public Base{

};

CRTP is typically used to implement static polymorphism. In contrast to dynamic polymorphism, this takes place at compile time and does not require any expensive pointer indirection.

C++98

Advertisement

The following crtp.cpp program presents an idiomatic implementation of CRTP based on C++98.

// crtp.cpp

#include

template
struct Base{
void interface(){ // (2)
static_cast(this)->implementation();
}
void implementation(){ // (3)
std::cout {
void implementation(){
std::cout {
void implementation(){
std::cout {}; // (4)

template // (1)
void execute(T& base){
base.interface();
}

int main(){

std::cout The function template execute (1) uses static polymorphism. The Base::interface(2) member function is the key of the CRTP idiom. The member function forwards the call to the derived class’s implementation: static_cast(this)->implementation. This is possible because the function is not instantiated until it is called. At this point, the derived classes Derived1, Derived2, and Derived3 are fully defined. Therefore, the Base::interface function can use the derived classes’ implementation. The member function Base::implementation (3) is interesting. It plays the role of a default implementation for the static polymorphism of the class Derived3 (4). The following image shows static polymorphism in action.

See also  Weak points in the cybersecurity of healthcare facilities

C++23

Thanks to the explicit object parameter, the C and the R can be removed from the acronym CRTP.

The deducingThisCRTP.cpp program introduces the C++23-based implementation of CRTP.

// deducingThisCRTP.cpp

#include

struct Base{ // (1)
template
void interface(this Self&& self){
self.implementation();
}
void implementation(){
std::cout
void execute(T& base){
base.interface(); // (2)
}

int main(){

std::cout The parameters of the Explicit object make it possible to infer the derived type and to forward it perfectly (1). For the specific type in (2), Derived1 (3), Derived2 (4), and Derived3 (5) are used. Consequently, the corresponding Virtual Function implementation called: std::forward(self).implementation(). The program can already be run with the current Microsoft compiler.

I received a comment on my last German post that I forgot the most illustrative uses of Deducing This: Recursive Lambdas. To be honest, I’m not so sure if this is the best use of Deducing This, because most programmers have trouble with recursion. Second, I’m not a fan of complicated lambdas. Lambdas should be concise and self-documenting.

Now I present different implementations of a recursively defined factorial function. After that, everyone can decide for themselves which version is the easiest to read.

Each function calculates the factorial of 10: 3628800.

C++98

In C++98 you have two options: either you use template metaprogramming with recursive instantiation or you use a function call. The template metaprogram is run at compile time.

// factorial_cpp98.cpp

#include

template
struct Factorial{
static int const value = N * Factorial::value;
};

template
struct Factorial{
static int const value = 1;
};

int factorial(unsigned int n){
return n > 0 ? n * factorial(n – 1): 1;
}

int main(){

See also  After the United States, Italy too: subscriptions are arriving on Instagram

std::cout ::value: ”
::value In the case of the template metaprogram, a full template specialization for the values 2 to 10 is created:

C++11

In C++11, the factorial function can be constexpr and has the potential to be run at compile time.

// factorial_cpp11.cpp

#include

constexpr int factorial(unsigned int n){
return n > 0 ? n * factorial(n – 1): 1;
}

int main(){

std::cout C++17

Thanks to constexp if, different code is generated depending on whether N > 0 or not. As with the template metaprogram in C++11, the compiler creates fully specialized templates for values ​​2 through 10:

// factorial_cpp17.cpp

#include

template
constexpr int factorial() {
if constexpr (N > 0)
return N * factorial();
else
return 1;
}

int main(){

std::cout () ” () A constexpr function (C++11) has the potential to run at compile time, but a consteval function (C++20) must run at compile time.

C++20

// factorial_cpp20.cpp

#include

consteval int factorial(unsigned int n){
return n > 0 ? n * factorial(n – 1): 1;
}

int main(){

std::cout Finally I ended up in C++23. In C++23, a lambda can refer to itself. This allows me to implement a recursive lambda.

C++23

// factorial_cpp23.cpp

#include

auto factorial = [](this auto&& self, unsigned int n) -> int {
return n > 0 ? n * self(n – 1): 1;
};

int main(){

std::cout The MSVC compiler does not yet fully support this deducing this. I therefore need to specify the return type (-> int) of the recursive lambda. According to proposal P0847R7, this is not necessary.

auto factorial = [](this auto&& self, unsigned int n) {
return n > 0 ? n * self(n – 1): 1;
};

Here is the output of the program:

Everyone may prefer a different variant of the factorial function. My favorite is the C++20 version based on consteval.

See also  The PS4 / PS5 / Switch version of "Simulated High Pressure Washing" is officially launched. The next wave of free DLC is confirmed as "Midgar's Special Commission" "PowerWash Simulator"

The core C++23 language offers more features than Deducing This. Exactly these features will be the subject of my next article. (rm)

To home page

You may also like

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More

Privacy & Cookies Policy