Short Version of Question
C++23 gives us a new way to write mixin classes (instead of CRTP). Is there any context where CRTP would still be preferred?
Summary of the Two Approaches
CRTP is a powerful idiom in C++ that leverages templates to automatically generate type-specific behavior at runtime. This is often used to create mixin classes.
C++23 introduces explicit object parameters, which allow the class name to be referenced explicitly in the parameter lists of non-static member functions. As a result, template non-static member functions can access the derived class, eliminating the need to specialize the mixin over the derived class.
Here is an example illustrating the old and new approach.
// classic CRTP (old approach)
template <typename T>
class MyMixin<T> {
// ...
void foo() {
// Stuff using T...
}
};
// have to specialize MyMixin over MyType
class MyType : public MyMixin<MyType> {
// ...
};
// explicit object parameter (C++23 approach)
class MyMixin {
// ...
template <typename T>
void foo(this T* self) {
// Stuff using T...
}
};
// better syntax for mixins
class MyType : public MyMixin {
// ...
};
Clearly, the new approach leads to cleaner syntax in the derived class. For instance, it lets us avoid gnarly things like this:
template<typename x, typename y, typename z, typename w>
class MyWackyContainer : public MyMixin<MyWackyContainer<x,y,z,w>>
since we don't have to provide a template parameter to MyMixin
with the new approach.
Full Question
Per the abstract of this paper (which addresses the previously mentioned problem),
While [adding explicit object parameters] has removed the need for using CRTP in many places, there still exist cases that must be expressed using this pattern.
The only functional difference I could think of between CRTP and the new approach, is that CRTP would let us reference the type when declaring non-function members and static member functions. But is there any situation where this is actually useful?
(Secondary question: Is my understanding correct? Did I miss anything?)
The big difference is that the CRTP base classes are distinct types, for which there is no equivalent type in the explicit object parameter version. Those types can be useful.
The static member thing is one example. The fact that you have distinct types allows you to implement static members that are only shared within a single derived class and not between all of them.
But also you may just need those types for whatever, as a place to put type alias definitions for example.