C++ 模板编程 CRTP 奇异递归模板模式

今天来聊聊 C++ Eigen 中的一个技巧——奇异递归模板模式(curiously recurring template pattern),简写为CRTP。
这篇博客也是我边看、边查、边理解的一个过程。如有错误,希望大神留言指正。

原来已经稍微接扫了几眼这个技术,在我粗浅的认识中,他在编译期通过模板,实现了大家用的比较多的运行时多态。我们来看看这个代码:

class Base
{
public:
    virtual void Do() = 0;
};

class Derived_1 : public Base
{
public:
    virtual void Do() override
    {
        cout << "DoWork 1" << endl;
    }
};

class Derived_2 : public Base
{
public:
    virtual void Do() override
    {
        cout << "DoWork 2" << endl;
    }
};

Base* p = new Derived_1();
p->Do();// output "DoWork 1"

p = new Derived_2();
p->Do();// output "DoWork 2"

众所周知,运行时多态的类保存了一个虚函数表,运行时通过查表确定具体调用的成员函数实现多态。增加了内存开销及CPU时间开销。那么通过模板,我们可以这样:

template<typename Derived>
class Base
{
public:
    void Do()
    {
        static_cast(this)->Do_imp();
    }
};

class Derived_1 : public Base
{
public:
    void Do_imp()
    {
        cout << "DoWork 1" << endl;
    }
};

class Derived_2 : public Base
{
public:
    void Do_imp()
    {
        cout << "DoWork 2" << endl;
    }
};

Derived_1* p1 = new Derived_1();
p1->Do();// output "DoWork 1"

Derived_2* p2 = new Derived_2();
p2->Do();// output "DoWork 2"

感觉跟运行时多态有那么一点点不太一样,但是问题不大,至少最后都是调用 Do 接口来获得了不同的性状。我们可以看到这样避免了虚函数的开销。

但这里有一个问题,我们的子类的实现 是一个 Public 的,这样破坏了类的封装性。我们其实并不想让用户看到我们 Do 函数的实现细节。
那么简单的想法是在子类中添加父类的友元。但这样在父类很多的时候照样很麻烦。

为了解决这样一个问题,我们引入一个新的类继承于Derived.

template<typename Derived>
class Base
{
public:
    void Do()
    {
        trait::Do(derived());
    }

    Derived& derived() { return static_cast(*this); }
private:
    struct trait : public Derived
    {
        static void Do(Derived& d)
        {
            auto funcp = &Derived::Do_imp;
            return (d.*funcp)();
        }
    };
};

class Derived_1 : public Base
{
protected:
    void Do_imp()
    {
        cout << "DoWork 1" << endl;
    }
};

class Derived_2 : public Base
{
protected:
    void Do_imp()
    {
        cout << "DoWork 2" << endl;
    }
};

这样 , 就可以在 Do 接口中调用 Derived 类中的 protected 函数了。

你可能感兴趣的:(其他学习笔记)