CRTP(Curiously Recurring Template Pattern)使用通用基础模板 class 而不是派生的 class

CRTP (Curiously Recurring Template Pattern) using a generic base template class instead of the derived class

我最近一直在研究 CRTP,并想出了一个使用 CRTP 创建通用基础模板的想法class。

// Example.h
namespace A {
    template <class TClass, typename T>
    class Example {
    public:
        Example(T &someStruct) : m_someStruct_(someStruct)
        {
        }

        ~Example() 
        {
            DoThis();
        }

    public:
        void DoThis()
        {
            static_cast<TClass*>(this)->DoThat(m_someStruct_);
        }

    private:
        T m_someStruct_;
    };
}

// AsArgument.h
namespace A {
    class AsArgument : public Example <AsArgument, SomeStruct> {
    friend class Example <AsArgument, SomeStruct>;
    private:
        void DoThat(SomeStruct &someFun)
        {
            // Do something to someFun object.
            // yehey(someFun);
            printf("I want to do that! \n");
        }
    };
}

我的目标是使用基础 class 对象访问派生的 class' 函数,同时通过仅包含基础 class 来分离基础和派生的实现]' 头文件并向前声明派生的 class 作为模板参数。

我知道我可以用不完整类型做什么的基础知识,但我似乎找不到有关模板的信息。

转发声明 class TDerived 参数而不包含头文件是否有效?

// SomeFile.cpp
#include "Example.h"

class A::AsArgument; // Forward declare this instead of including the AsArgument.h header file

namespace B {
    void SomeClass::DoSomething()
    {
        SomeStruct fun;
        Example <AsArgument, SomeStruct> example(fun);
    }
}

我不确定这是否是创建通用基础模板的好设计 class,但我在建立基础 class 后的目标是轻松创建派生的 classes从中取出并在编译时定义基础 class 实现。 它实际上是 RAII 和 CRTP 的某种组合。

我实际上可以通过将 "AsArgument.h" 文件包含在 "Example.h" 中来实现这一点,但是基础和实现之间的分离丢失了。当我尝试转发声明 AsArgument class 时,我不断收到编译错误(可能是因为我没有完全意识到的名称空间问题)。

任何建议或这种设计是否有效有效?

我不太确定这里的设计目标是什么,但是关于不完整类型的规则以同样的方式适用,无论你是否在谈论模板,你只需要考虑模板在哪里被实例化.

在你的例子中,你试图避免在 SomeFile.cpp 中包含 AsArgument.h。但是,您使用 AsArgument class 实例化示例 class 模板。这意味着当您编译 SomeFile.cpp 时,该翻译单元对 AsArgument class 一无所知(因为它在 .h 文件中看不到它的声明),只知道它存在。

但是,如您所料,如果您只知道 class 存在,您将无能为力。你甚至不能按价值持有它,因为你不知道它的大小。您不能使用它的任何界面。在您的示例中,编译器无法知道 AsArgument::DoThat 甚至存在(它不一定需要知道它的作用,可以留给链接器)。请记住,Example 是在 SomeFile.cpp 中实例化的,因此编译器需要知道 DoThat 存在的位置。

所以你需要AsArgument.h。对于正常的 class,您可以将声明放在 .h 文件中,并将定义(实现)放在 .cpp 文件中。但是 AsArgument 是一个模板class,所以一般情况下你不能那样做。如果您在预先知道的有限数量的 classes 上进行模板化,并且愿意在所有这些上明确地进行模板化,则只能对模板执行此操作。

我不能对大局发表太多评论,因为我不知道你想做什么。我不确定 CRTP 是否适合您。 CRTP 在某些方面很有用,但它并不是我真正使用的第一个工具。其实现在想想:我很少用。在大多数情况下,如果我要使用基于模板的多态性,我可以直接持有 "child" 并完全跳过基数,在很多情况下我觉得基数对我来说不够。

我建议在以后的 SO 问题中包括您遇到的任何编译器错误。祝你好运!