我正在努力研究 CRTP。周围有一些很好的资源,包括这个论坛,但我想我对静态多态性的基础知识有些困惑。查看以下维基百科条目:
template <class T>
struct Base
{
void implementation()
{
// ...
static_cast<T*>(this)->implementation();
// ...
}
static void static_func()
{
// ...
T::static_sub_func();
// ...
}
};
struct Derived : public Base<Derived>
{
void implementation();
static void static_sub_func();
};
我知道这有助于我在派生类中有不同的 implementation() 变体,有点像编译时虚函数。但是,我的困惑是我认为我不能拥有像
这样的功能void func(Base x){
x.implementation();
}
因为 Base 是模板化的,所以我会使用普通的继承和虚函数,但我必须指定
func(Derived x)
或使用
template<class T>
func(T x)
那么 CRTP 在这种情况下实际上给我带来了什么,而不是简单地在 Derived::Base 中直接隐藏/实现方法?
struct Base
{
void implementation();
};
struct Derived : public Base
{
void implementation();
static void static_sub_func();
};
最佳答案
问题在于,关于 CRPT 的实际用途,将 CRTP 描述为“静态多态性”并没有真正的帮助或准确。多态性实际上就是让不同的类型实现相同的接口(interface)或契约;这些不同类型如何实现该接口(interface)与多态性是正交的。动态多态看起来像这样:
void foo(Animal& a) { a.make_sound(); } // could bark, meow, etc
其中 Animal
是一个提供虚拟 make_sound
方法的基类,即 Dog
、Cat
等,覆盖。这是静态多态性:
template <class T>
void foo(T& a) { a.make_sound(); }
就是这样。您可以在恰好定义了 make_sound
方法的任何类型上调用 foo
的静态版本,而无需从基类继承。并且该调用将在编译时解析(即您无需为 vtable 调用付费)。
那么 CRTP 适合什么地方呢? CRTP 实际上根本与接口(interface)无关,因此与多态性无关。 CRTP 是为了让您更轻松地实现事情。 CRTP 的神奇之处在于它可以将事物直接注入(inject)到类型的接口(interface)中,并且完全了解派生类型提供的所有内容。一个简单的例子可能是:
template <class T>
struct MakeDouble {
T double() {
auto& me = static_cast<T&>(*this);
return me + me;
};
现在任何定义加法运算符的类,也可以被赋予一个double
方法:
class Matrix : MakeDouble<Matrix> ...
Matrix m;
auto m2 = m.double();
CRTP 完全是为了帮助实现,而不是接口(interface)。所以不要太在意它通常被称为“静态多态性”这一事实。如果您想要有关 CRTP 的用途的真正规范示例,请考虑 Andrei Alexandrescu 的现代 C++ 设计的第 1 章。不过,慢慢来 :-)。
关于c++ - 关于CRTP静态多态的困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43821541/