C++ CRTP介绍

目录

一、什么是CRTP

二、CRTP的优点

三、CRTP的应用

3.1 静态多态

3.2 多态链(Polymorphic chaining)

​编辑

3.3多态的复制构造体(Polymorphic copy construction)


一、什么是CRTP

CRTP有两大特性:

  • 继承自模板类;
  • 派生类将自身作为参数传给模板类。
// 我们先定义一个模板类作为基类
template <typename T>
class Base
{
    ...
};
 // 定义一个派生类,这个类继承以自身作为参数的基类
class Derived : public Base<Derived>
{
    ...
};

二、CRTP的优点

这样做的目的其实很明确,从基类对象的角度来看,派生类对象其实就是本身,这样的话只需要用一个static_cast就可以把基类转化成派生类,从而实现基类对象对派生对象的访问。

template <typename T>
class Base
{
public:
    void doSomething()
    {
        T& derived = static_cast<T&>(*this);
    }
};

class Derived : public Base<Derived>
{
public:
    void doSomething()
    {
         std::cout << " Derived class " << std::endl;
    }  
};

这里将基类转换成派生类用的是static_cast静态绑定,而普通基类转派生类用的是dynamic_cast动态绑定。动态绑定的目的是为了确保你所转化的派生类是正确的,而对于CRTP来说,基类是继承于模板类的参数,也就是派生类本身。这也正是CRTP这种设计的目的。

多态是个很好的特性,但是动态绑定比较慢,因为要查虚函数表。而使用 CRTP,完全消除了动态绑定,降低了继承带来的虚函数表查询开销。

三、CRTP的应用

3.1 静态多态

template <class T> 
struct Base
{
    void interface()
    {
        // ...
        static_cast<T*>(this)->implementation();
        // ...
    }

    static void static_func()
    {
        // ...
        T::static_sub_func();
        // ...
    }
};

struct Derived : Base<Derived>
{
    void implementation();

    static void static_sub_func();
};

Base<Derived>::interface() 为例,在Derived : Base<Derived>中,Base<Derived>是先于Derived而存在的,所以当Base<Derived>::interface()被申明时,编译器并不知道Derived的存在的,但由于此时 Base<Derived>::interface() 并不会被实例化。只有当Base<Derived>::interface()被调用时,才会被实例化,而此时编译器也已经知道了 Derived::implementation()的声明了。这里等同于通过查询虚函数动态绑定以达到多态的效果,但省略了动态绑定虚函数查询的时间。

3.2 多态链(Polymorphic chaining)

// print1.cpp
#include <iostream>
using namespace std;

class Printer
{
public:
    Printer(ostream& pstream) : m_stream(pstream) {}

    template <typename T>
    Printer& print(T&& t) { m_stream << t; return *this; }

    template <typename T>
    Printer& println(T&& t) { m_stream << t << endl; return *this; }
private:
    ostream& m_stream;
};

class CoutPrinter : public Printer
{
public:
    CoutPrinter() : Printer(cout) {}

    CoutPrinter& SetConsoleColor(string s)
    {
        // ...
        return *this;
    }
};
int main()
{
    CoutPrinter().print("Hello ").SetConsoleColor("red").println("Printer!");
    return 0;
}

上面Printer定义打印的方法,CoutPrinterPrinter的子类,并且添加了一个设置打印颜色的方法。前半段CoutPrinter().print("Hello ")调用的是Printer实例,后面接着SetConsoleColor(Color.red)实际上又需要调用CoutPrinter实例,这样编译器就会报错。

而CRTP就可以很好的解决这个问题,代码如下:

// print2.cpp
#include <iostream>
using namespace std;

// Base class
template <typename ConcretePrinter>
class Printer
{
public:
    Printer(ostream& pstream) : m_stream(pstream) {}

    template <typename T>
    ConcretePrinter& print(T&& t)
    {
        m_stream << t;
        return static_cast<ConcretePrinter&>(*this);
    }

    template <typename T>
    ConcretePrinter& println(T&& t)
    {
        m_stream << t << endl;
        return static_cast<ConcretePrinter&>(*this);
    }
private:
    ostream& m_stream;
};

// Derived class
class CoutPrinter : public Printer<CoutPrinter>
{
public:
    CoutPrinter() : Printer(cout) {}

    CoutPrinter& SetConsoleColor(string s)
    {
        // ...
        return *this;
    }
};
int main()
{
    // usage
    CoutPrinter().print("Hello ").SetConsoleColor("red").println("Printer!");
    return 0;
}

3.3多态的复制构造体(Polymorphic copy construction)

当我们用到多态时,经常会需要通过基类的指针来复制子对象。通常我们可以通过在基类里面构造一个clone()虚函数,然后在每个子类里面定义这个clone()函数。使用CRTP可以让我们避免反复地在子类中去定义clone()函数。

// Base class has a pure virtual function for cloning
class AbstractShape {
public:
    virtual ~AbstractShape () = default;
    virtual std::unique_ptr<AbstractShape> clone() const = 0;
};

// This CRTP class implements clone() for Derived
template <typename Derived>
class Shape : public AbstractShape {
public:
    std::unique_ptr<AbstractShape> clone() const override {
        return std::make_unique<Derived>(static_cast<Derived const&>(*this));
    }

protected:
   // We make clear Shape class needs to be inherited
   Shape() = default;
   Shape(const Shape&) = default;
};

// Every derived class inherits from CRTP class instead of abstract class

class Square : public Shape<Square>{};

class Circle : public Shape<Circle>{};

不使用CRTP,使用普通写法的话需要分别在派生类里定义clone函数才可以。

​​​​​​​

参考:c++ CRTP(奇异的递归模板模式)介绍  c++ 中CRTP的用途

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值