CRTP 与 std::variant

警告
本文最后更新于 2024-05-01,文中内容可能已过时。

Curriously Recursive Template Method(CRTP) 一种是实现了编译期多态(静态多态)的方法,相比于虚函数(virtual)跳过了虚表vtable查找,提供了比动态多态(运行时多态)更好的性能。

demo

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <functional>
#include <vector>
#include <iostream>
#include <unordered_map>
#include <string>
#include <utility>
#include <variant>
using namespace std;

template<typename T>
struct B
{
public:
    using cb_t = std::function<void()>;

    B() { _cbs.reserve(10); }
    virtual ~B() { cout << "~B" << endl; }

    void foo() noexcept
    {
        this->get()->foo_impl();
        for (const auto& cb : _cbs)
            cb();
    }
    void register_cb(cb_t cb) noexcept
    {
        _cbs.push_back(cb);
    }

private:
    T* get() noexcept { return static_cast<T*>(this); }
    std::vector<cb_t> _cbs;
};

struct D1: public B<D1>
{
    ~D1() { cout << "~D1" << endl; }
    void foo_impl() noexcept { cout << "D1:foo_impl" << endl; }
};

struct D2: public B<D2>
{
    ~D2() { cout << "~D2" << endl; }
    void foo_impl() noexcept { cout << "D2:foo_impl" << endl; }
};


template<typename T>
void execute(B<T>* e)
{
    e->foo();
}

int main()
{
    B<D1>* d1 = new D1;
    d1->register_cb([]()
    {
        cout << "[d1] cb called" << endl;
    });

    B<D2>* d2 = new D2;
    d2->register_cb([]()
    {
        cout << "[d2] cb called" << endl;
    });

    std::unordered_map<std::string, std::variant<B<D1>*, B<D2>*>> m;
    m["d1"] = d1;
    m["d2"] = d2;

    //use variant
    {
        auto d = std::get<B<D1>*>(m["d1"]);
        d->foo();
    }

    //use variant
    {
        auto d = std::get<B<D2>*>(m["d2"]);
        d->foo();
    }

    //policy-based ctx
    execute(d1);
    execute(d2);

    delete d1;
    delete d2;

    return 0;
}

运行结果

1
2
3
4
5
6
D1:foo_impl
[d1] cb called
D2:foo_impl
[d2] cb called
~D1
~D2

Tips

  • cb 需要绑定 lambda 表达式的引用或者函数指针,编译器有可能认为这是同一个代码段,导致上面的 cb 一直打印 d2 cb
william 支付宝支付宝
william 微信微信
0%