Practical Uses for the “Curiously Recurring Template Pattern”
"奇怪的重复模板模式"有哪些实际用途?通常显示的"计数类"例子对我来说并不是一个令人信服的例子。
它对于mixin(我指的是从中继承的类以提供功能)也特别有用,因为mixin本身需要知道它们在操作什么类型(因此需要是模板)。
在有效的C++中,Scott Meyers提供了一个类模板NeWHANDLReals
如果没有crtp,就不能真正做到这一点,因为需要单独实例化newhandlersupport模板,每个使用它的类都有一个单独的静态数据成员来存储当前的新处理程序。
显然,整个示例是非常非线程安全的,但它说明了这一点。
迈耶斯建议CRTP可以被认为是"为我做"。我想说的是,这通常适用于任何mixin,而crtp适用于需要mixin模板而不仅仅是mixin类的情况。
模拟动态绑定。避免虚拟函数调用的成本,同时保留一些层次结构的好处,对于子系统来说是一个巨大的胜利,在我目前正在进行的项目中可以做到这一点。
如果您认为只有在方法扩展时才需要传递给超类的子类类型,那么CRTP就不那么好奇了。然后定义所有类型。您只需要模式将符号子类类型导入到超类中,但就超类而言,它只是一个前向声明——因为所有正式模板参数类型都是由定义决定的。
我们以稍微修改的形式使用,将traits类型结构中的子类传递给超类,以使超类能够返回派生类型的对象。该应用程序是一个几何微积分库(点、向量、线、框),其中所有的通用功能都在超类中实现,子类只定义了一个特定的类型:cfltpoint继承自tgenpoint。cfltpoint也存在于tgenpoint之前,因此子类化是一种自然的重构方法。
要想真正使用crtp库,请看atl和wtl(wtl.sf.net)。在那里它被广泛用于编译时多态性。
通常,它用于类似多态的模式,在这些模式中,您不需要在运行时(仅在编译时)选择派生类。这可以节省运行时虚拟函数调用的开销。
它有点像C宏:利用这个宏不是在定义时编译的,而是在使用时编译的。
1 | #define CALL_THE_RIGHT_FOO foo() |
文件A:
1 2 3 4 5 6 | static void foo() { // do file A thing } ... CALL_THE_RIGHT_FOO ... |
文件A:
1 2 3 4 5 6 | static void foo() { // do file B thing } ... CALL_THE_RIGHT_FOO ... |
您描述的模板使用模式允许我们在父模板中"调用正确的foo",从而推迟正确foo的定义,直到模板被实例化。除此之外,这是基于父级中t值的classa::foo和classb::foo之间的区别。