考虑以下标准CRTP示例:
g
如果这是常规虚拟继承,我可以将虚拟struct Base {
virtual void f() = 0;
virtual void g() = 0;
};
和Foo
方法标记为纯粹
this->f
并获得关于static_cast<Derived *>(this)->f
抽象的编译时错误。但是CRTP没有提供这样的保护。我可以以某种方式实现它吗?运行时检查也是可以接受的。我考虑过将shmat()
指针与void*
进行比较,但无法使其正常工作。
答案 0 :(得分:24)
您可以在编译时断言成员函数的两个指针是不同的,例如:
template<class Derived>
struct Base {
void g() {
static_assert(&Derived::g != &Base<Derived>::g,
"Derived classes must implement g().");
static_cast<Derived *>(this)->g();
}
};
答案 1 :(得分:12)
你可以使用这个解决方案,你可以拥有纯粹的“非虚拟抽象”功能,并尽可能地映射到CRTP recommendation of H. Sutter:
template<class Derived>
struct Base
{
void f(){static_cast<Derived*>(this)->do_f();}
void g(){static_cast<Derived*>(this)->do_g();}
private:
//Derived must implement do_f
void do_f()=delete;
//do_g as a default implementation
void do_g(){}
};
struct derived
:Base<derived>
{
friend struct Base<derived>;
private:
void do_f(){}
};
答案 2 :(得分:12)
这是另一种可能性:
#include <iostream>
template<class Derived>
struct Base {
auto f() { return static_cast<Derived *>(this)->f(); }
auto g() { return static_cast<Derived *>(this)->g(); }
};
struct Foo : public Base<Foo> {
void f() { std::cout << 42 << std::endl; }
};
int main() {
Foo foo;
foo.f(); // just OK
foo.g(); // this will not compile
}
对于GCC,它提供了一个非常明确的错误消息(“错误:在扣除'auto'”之前使用'auto Base :: g()[with Derived = Foo]',而对于Clang来说,它提供了一个稍微不那么可读的无限递归模板实例化Base<Foo>::g
,g
实例化自身,但最终以错误结束。
答案 3 :(得分:0)
你可以考虑做这样的事情。您可以将Derived
作为成员,并在每次实例化Base
时直接将其作为模板参数提供,或者像我在此示例中所做的那样使用类型别名:
template<class Derived>
struct Base {
void f() { d.f(); }
void g() { d.g(); }
private:
Derived d;
};
struct FooImpl {
void f() { std::cout << 42 << std::endl; }
};
using Foo = Base<FooImpl>;
int main() {
Foo foo;
foo.f(); // OK
foo.g(); // compile time error
}
当然Derived
不再是派生的,因此您可以为它选择一个更好的名称。