C++:CRTP,传入继承

文章来自微信公众号“科文路”,欢迎关注、互动。转发须注明出处。

由于名称 CRTP 没啥内涵也不好记,我这里就称其为“传入继承”吧。“传入”可以助记 CR TP,也指代需要把自身传进父类。

MLIR 中使用 C++ 定义新的 Op 时,就避不开 CRTP (Curiously Recurring Template Pattern) 这么个神奇的 C++ 设计模式。

比如下面这段代码

1
2
3
4
5
6
7
class ConstantOp : 
public mlir::Op<
ConstantOp,
mlir::OpTrait::ZeroOperands,
mlir::OpTrait::OneResult,
mlir::OpTraits::OneTypedResult<TensorType>::Impl> {
...

可以看到,我们需要定义的 ConstantOp 出现在了右侧父类的模板参数列表中。果然 curiously enough (神奇)。

这里借用 cppreference 的例子一起学习一下这个需要把自身交代给父类的继承方式,由于原始名称 CRTP (奇异递归模板模式)没啥内涵也不好记,我这里就称其为“传入继承”吧。“传入”可以助记 CR TP,也指代需要把自身传进父类。

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
// CRTP.cpp
#include <iostream>

template <class Derived> struct Base {
void name() { (static_cast<Derived *>(this))->impl(); }
};

struct D1 : public Base<D1> {
void impl() { std::cout << "D1::impl()\n"; }
};
struct D2 : public Base<D2> {
void impl() { std::cout << "D2::impl()\n"; }
};

int main() {
Base<D1> b1;
b1.name();
Base<D2> b2;
b2.name();

D1 d1;
d1.name();
D2 d2;
d2.name();
}
1
2
3
4
❯ clang++ -o CRTP CRTP.cpp
❯ ./CRTP
D1::impl()
D2::impl()

例子中,

  • 基类 Base 将模板参数 Derived 设置为接口 name()static_cast<Derived*> 的模板参数;
  • 子类在继承时,将自身作为模板参数传入父类

结果符合预期吗?重点关注的地方是最后四行,在声明 D1,D2 后,直接就能把 name() 给用了。

其实也不用深究为什么这样可以 work,它就是个合法的语法嘛​,掌握了就行了。

这么做的收益是实现了编译时多态(静态多态),从而简化代码、提高性能。因为如果使用虚函数,将产生多余的调度开销

同样类似蹊跷的还有 enable_shared_from_this

1
2
3
4
5
6
struct Me: std::enable_shared_from_this<Me>
{
std::shared_ptr<Me> getptr() {
return shared_from_this();
}
};

都看到这儿了,不如关注每日推送的“科文路”、互动起来~

至少点个赞再走吧~

Author

xlindo

Posted on

2022-08-04

Updated on

2023-05-10

Licensed under

Comments