首页 > 解决方案 > CRTP 与模板专业化和标签调度

问题描述

我试图找出 CRTP 习语相对于模板专业化的优缺点。假设我想创建一个“渲染器”类,渲染器将使用“Vulkan”或“OpenGL”实现。

使用 CRTP:

template <typename T>
class Renderer
{
public:
    void draw(Shape s) { static_cast<T*>(this)->drawImpl(s); };
};

class VulkanRenderer : public Renderer<VulkanRenderer>
{
public:
    void drawImpl(Shape s) { /* Vulkan implementation */};
};

class OpenGLRenderer : public Renderer<OpenGLRenderer>
{
public:
    void drawImpl(Shape s) { /* OpenGL implementation */ };
};

使用模板特化和类标签:

class Vulkan;
class OpenGL;

template<typename T>
class Renderer
{
public:
    Renderer() { assert(false, "Type not support"); }
    void draw(Shape s);
};

template<>
class Renderer<Vulkan>
{
public:
    void draw(Shape s) { /* Vulkan implementation */ };
};

template<>
class Renderer<OpenGL>
{
public:
    void draw(Shape s) { /* OpenGL implementation */ };
};

一种方法相对于另一种方法是否有某些优势?是否有某些事情我可以用一个做而我不能用另一个做?静态多态性技术有什么替代品吗?

此外,这些方法中的任何一种简单地定义单独的类(例如“VulkanRenderer”和“OpenGLRenderer”)是否有任何优势。此外,概念可能不是这两者的更好方式吗?

标签: c++

解决方案


使用 CRTP,您可以将任何共享代码/类型/常量等放入基本模板,同时支持这两种实现。如果您没有要分享的内容,那么使用 CRTP 毫无意义。它确实具有通常易于管理的缺点,即动态创建的派生对象有可能通过基类指针意外或粗心删除,从而导致未定义的行为。

使用类标签,与拥有两个不相关的类的唯一区别是,它更容易约束其他模板以需要某种类型的渲染器(即template <typename ARenderer> void f(Renderer<ARenderer>& r);),而不是对不相关的类有要求std::is_same_v<T, OpenGLRenderer> || std::is_same_v<T, VulcanRenderer>- 没有太多实用程序。如果渲染器除了接口的语义之外真的没有任何共同点,那么这是一个选择。


推荐阅读