CRTP (Curiously Recurring Template Pattern) using a generic base template class instead of the derived class
我最近一直在研究crtp,并想出了一个使用crtp创建通用基本模板类的想法。
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 | // 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! "); } }; } |
我的目标是使用基类对象访问派生类的函数,同时通过只包含基类的头文件并将派生类作为模板参数转发来分离基类和派生类的实现。
我知道如何处理不完整类型的基本知识,但似乎找不到有关模板的信息。
转发声明类TDerived参数而不是包含头文件是否有效?
1 2 3 4 5 6 7 8 9 10 11 12 | // 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); } } |
我不确定在创建通用的基本模板类时,这是否是一个好的设计,但是我在创建基本类之后的目标是轻松地从中创建派生类,并在编译时定义基本类实现。它实际上是RAII和CRTP的某种组合。
实际上,我可以通过将"asargument.h"文件包含在"example.h"中来实现这一点,但是基础和实现之间的分离将丢失。当我试图转发声明asargument类时,我总是会得到编译错误(可能是因为我不完全知道的名称空间问题)。
有什么建议,或者这种设计是否有效?
我不确定这里的设计目标是什么,但是关于不完整类型的规则同样适用,无论您是否谈论模板,您只需要考虑模板的实例化位置。
在您的案例中,您试图避免在somefile.cpp中包含asargument.h。但是,您可以用asargument类实例化示例类模板。这意味着当您编译somefile.cpp时,翻译单元对asargument类一无所知(因为它在.h文件中看不到它的声明),只知道它存在。
但是,正如您可能期望的那样,如果您只知道某个类存在,那么就不能对它做太多的工作。你甚至不能用价值来衡量它,因为你不知道它的大小。您不能使用它的任何接口。在您的示例中,编译器不可能知道asArgument::dot甚至存在(它不一定需要知道它做了什么,可以留给链接器)。请记住,这个示例是在somefile.cpp中实例化的,所以编译器需要知道它的存在。
所以您需要asargument.h。对于一个普通类,您可以将声明放在.h文件中,并将定义(实现)放在.cpp文件中。但是asArgument是一个模板类,所以一般情况下不能这样做。只有在预先知道的有限数量的类上模板化并且愿意在所有类上显式模板化时,才能对模板执行此操作。
我不能对全局作太多评论,因为我不知道你在做什么。我不太相信CRTP适合你。crtp对于某些事情是有用的,但它并不是我真正使用的第一个工具。实际上,现在我想起来了:我很少使用它。在大多数情况下,如果我要使用基于模板的多态性,我可以直接抱着"孩子",完全跳过基础,在许多情况下,我觉得基础不够买我。
我建议您在以后的问题中包括任何编译器错误。祝你好运!