递归模板模式--The curiously recurring template pattern

1  The curiously recurring template pattern

1.1 General form

// The Curiously Recurring Template Pattern (CRTP)
  
  
template <typename derived>
  
  
struct base
  
  
{
  
  
    // ...
  
  
};
  
  
struct derived : base<derived>
  
  
{
  
  
    // ...
  
  
};
  
  

Some use cases for this pattern are static polymorphism, and other metaprogramming techniques such as those described by Alexandrescu in Modern C++ Design [2].

1.2 [edit] Static polymorphism

Typically, the base class template will take advantage of the fact that member function bodies (definitions) are not instantiated until long after their declarations, and will use members of the derived class within its own member functions, via the use of a static_cast, or simply a cast e.g.:

template <class Derived> struct Base
  
  
{
  
  
    void interface()
  
  
    {
  
  
        // ...
  
  
        static_cast<Derived*>(this)->implementation();
  
  
        // ...
  
  
    }
  
  
 
  
  
    static void static_func()
  
  
    {
  
  
        // ...
  
  
        Derived::static_sub_func();
  
  
        // ...
  
  
    }
  
  
};
  
  
 
  
  
struct Derived : Base<Derived>
  
  
{
  
  
    void implementation();
  
  
    static void static_sub_func();
  
  
};
  
  

This technique achieves a similar effect to the use of virtual functions, without the costs (and some flexibility) of dynamic polymorphism. This particular use of the CRTP has been called "simulated dynamic binding" by some[3]. This pattern is used extensively in the Windows ATL and WTL libraries.

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 inherit from this class, a derived class, we inherit all the member variables and member functions that weren't overridden (no constructors or destructors, of course, either). If the derived class calls an inherited function that then calls another member function, that function will never call any derived or overridden member functions in the derived class. As a result of this behavior, most C++ programmers define member functions as virtual to avoid this problem.

However, if base class member functions use the CRTP pattern for all member function calls, the overridden functions in the derived class will get selected at compile time. This effectively emulates the virtual function call system at compile time without the costs in size or function call overhead (VTBL structures, and method lookups, multiple-inheritance VTBL machinery) and the slight disadvantage of not being able to do this choice at runtime.

1.3 [edit] Object counter

The main purpose of an object counter is retrieving statistics of object creation and destruction for a given class. This can be easily solved using CRTP:

template <typename T>
  
  
struct counter
  
  
{
  
  
    counter()
  
  
    {
  
  
        objects_created++;
  
  
        objects_alive++;
  
  
    }
  
  
 
  
  
    virtual ~counter()
  
  
    {
  
  
        --objects_alive;
  
  
    }
  
  
    static int objects_created;
  
  
    static int objects_alive;
  
  
};
  
  
template <typename T> int counter<T>::objects_created( 0 );
  
  
template <typename T> int counter<T>::objects_alive( 0 );
  
  
 
  
  
class X : counter<X>
  
  
{
  
  
    // ...
  
  
};
  
  
 
  
  
class Y : counter<Y>
  
  
{
  
  
    // ...
  
  
};
  
  

Each time an object of class X is created, the constructor of counter<X> is called, incrementing both the created and alive count. Each time an object of class X is destroyed, the alive count is decremented. In this example, none of the functionality in the template uses the template parameter, and it is only used as a way of creating different static counters for different classes. Remember that each instantiation of a template with a type argument is a class of itself, and thus the static counter attributes in the counter template are different offering separated counters for X and Y classes.

 

int main() {

CountedClass a;

cout << CountedClass::getCount() << endl; // 1

CountedClass b;

cout << CountedClass::getCount() << endl; // 2

CountedClass 2 c ;

cout << CountedClass2::getCount() << endl; // 1 (!)

} ///:~

 

Each derived class derives from a unique base class that is determined by using itself (the derived class) as a template parameter! This may seem like a circular definition, and it would be, had any base class members used the template argument in a computation. Since no data members of Counted are dependent on T, the size of Counted (which is zero!) is known when the template is parsed. So it doesn t matter which argument is used to instantiate Counted because the size is always the same. Any derivation from an instance of Counted can be completed when it is parsed, and there is no recursion. Since each base class is unique, it has its own static data, thus constituting a handy technique for adding counting to any class whatsoever. Jim Coplien was the first to mention this interesting derivation idiom in print, which he cited in an article, entitled Curiously Recurring Template Patterns.

 

1.4 [edit] In other languages

The CRTP makes an appearance in the Java programming language standard library where the Enum class is defined as Enum<T extends Enum<T>>. Java programmers use the CRTP in practice when they write a comparable class: class X implements Comparable<X>.

1.5 [edit] See also

1.6 [edit] References

  1. ^ Coplien, James O. (1995, February). "Curiously Recurring Template Patterns". C++ Report: 24–27. 
  2. ^ Andrei Alexandrescu: Modern C++ Design: Generic Programming and Design Patterns Applied, Addison-Wesley, ISBN 3-8266-1347-3
  3. ^ http://www.pnotepad.org/devlog/archives/000083.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值