Content deleted Content added
Added mention to real world (STL) use of CRTP |
m Dating maintenance tags: {{Clarify}} |
||
(12 intermediate revisions by 11 users not shown) | |||
Line 4:
==History==
The technique was formalized in 1989 as "''F''-bounded quantification."<ref>{{cite web|url=http://
The Microsoft Implementation of CRTP in [[Active Template Library]] (ATL) was independently discovered, also in 1995, by Jan Falkin, who accidentally derived a base class from a derived class. Christian Beaumont first saw
== General form ==
Line 22 ⟶ 21:
// ...
};
</syntaxhighlight>{{clarify|reason=members within Base<T> can use what template and what would that look like?|date=June 2025}}
</syntaxhighlight>▼
Some use cases for this pattern are [[Template metaprogramming#Static polymorphism|static polymorphism]] and other metaprogramming techniques such as those described by [[Andrei Alexandrescu]] in ''[[Modern C++ Design]]''.<ref>{{cite book | first=Andrei | last=Alexandrescu | authorlink=Andrei Alexandrescu | title=Modern C++ Design: Generic Programming and Design Patterns Applied | publisher=Addison-Wesley | isbn=0-201-70431-5 | year=2001}}</ref>
Line 51 ⟶ 50:
};
struct Derived : public Base<Derived>
{
void implementation();
Line 58 ⟶ 57:
</syntaxhighlight>
In the above example, the function <code>Base<Derived>::
This technique achieves a similar effect to the use of [[virtual function]]s, without the costs (and some flexibility) of [[dynamic polymorphism]]. This particular use of the CRTP has been called "simulated dynamic binding" by some.<ref>{{cite web | url=http://www.pnotepad.org/devlog/archives/000083.html | title=Simulated Dynamic Binding | date=7 May 2003 | accessdate=13 January 2012 | url-status=dead | archiveurl=https://web.archive.org/web/20120209045146/http://www.pnotepad.org/devlog/archives/000083.html | archivedate=9 February 2012 }}</ref> This pattern is used extensively in the Windows [[Active Template Library|ATL]] and [[Windows Template Library|WTL]] libraries.
Line 64 ⟶ 63:
To elaborate on the above example, consider a base class with '''no virtual functions'''. Whenever the base class calls another member function, it will always call its own base class functions. When we derive a class from this base class, we inherit all the member variables and member functions that were not overridden (no constructors or destructors). If the derived class calls an inherited function which then calls another member function, then that function will never call any derived or overridden member functions in the derived class.
However, if base class member functions use CRTP for all member function calls, the overridden functions in the derived class will be selected at compile time. This effectively emulates the virtual function call system at compile time without the costs in size or function call overhead ([[Virtual method table|VTBL]] structures, and method lookups, multiple-inheritance VTBL machinery) at the disadvantage of not being able to make this choice at runtime.
== Object counter ==
Line 244 ⟶ 243:
===Pitfalls===
One issue with static polymorphism is that without using a general base class like <code>AbstractShape</code> from the above example, derived classes cannot be stored homogeneously – that is, putting different types derived from the same base class in the same container. For example, a container defined as <code>std::vector<Shape*></code> does not work because <code>Shape</code> is not a class, but a template needing specialization. A container defined as <code>std::vector<Shape<Circle>*></code> can only store <code>Circle</code>s, not <code>Square</code>s. This is because each of the classes derived from the CRTP base class <code>Shape</code> is a unique type. A common solution to this problem is to inherit from a shared base class with a virtual destructor, like the <code>AbstractShape</code> example above, allowing for the creation of a <code>std::vector<AbstractShape*></code>.
==Deducing this==
The use of CRTP can be simplified using the [[C++23]] feature ''deducing this''.<ref>{{Cite web|url=http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html|title=Deducing this|date=2021-07-12|author1=Gašper Ažman|author2=Sy Brand|author3=Ben Deane|author4=Barry Revzin
}}</ref><ref>{{Cite web|title=Explicit object parameter|url=https://en.cppreference.com/w/cpp/language/member_functions#Explicit_object_parameter|access-date=27 December 2023}}</ref> For the function <code>signature_dish</code> to call a derived member function <code>cook_signature_dish</code>, <code>ChefBase</code> needs to be a templated type and <code>CafeChef</code> needs to inherit from <code>ChefBase</code>, passing its type as the template parameter.
<syntaxhighlight lang="cpp">
template <typename T>
struct ChefBase
{
void signature_dish()
{
static_cast<T*>(this)->cook_signature_dish();
}
};
struct CafeChef : ChefBase<CafeChef>
{
void cook_signature_dish() {}
};
▲</syntaxhighlight>
If explicit object parameter is used, <code>ChefBase</code> does not need to be templated and <code>CafeChef</code> can derive from <code>ChefBase</code> plainly. Since the <code>self</code> parameter is automatically deduced as the correct derived type, no casting is required.
<syntaxhighlight lang="cpp">
struct ChefBase
{
template <typename Self>
void signature_dish(this Self&& self)
{
self.cook_signature_dish();
}
};
struct CafeChef : ChefBase
{
void cook_signature_dish() {}
};
</syntaxhighlight>
==See also==
|