在CRTP to avoid dynamic polymorphism中,提出以下解决方案以避免虚拟成员函数的开销并强加特定接口:
template <class Derived>
struct base {
void foo() {
static_cast<Derived *>(this)->foo();
};
};
struct my_type : base<my_type> {
void foo() {}; // required to compile. < Don't see why
};
struct your_type : base<your_type> {
void foo() {}; // required to compile. < Don't see why
};
然而,似乎派生类不需要编译定义,因为它继承了一个(代码编译得很好而没有定义my_type :: foo)。实际上,如果提供了一个函数,则在使用派生类时不会调用基函数。
所以问题是,以下代码替换是否可接受(和标准?):
template <class Derived>
struct base {
void foo() {
// Generate a meaningful error if called
(void)sizeof( Derived::foo_IS_MISSING );
};
};
struct my_type : base<my_type> {
void foo() {}; // required to compile.
};
struct your_type : base<your_type> {
void foo() {}; // required to compile.
};
int main() {
my_type my_obj;
my_obj.foo(); // will fail if foo missing in derived class
}
答案 0 :(得分:4)
据我所知,这种模式的重点是,您可以简单地将参数传递为template <typename T> base<T> &
,并且您的接口由base<T>
中的(非虚拟)函数定义。如果您没有要定义的界面(正如您在问题的第二部分中所建议的那样),那么首先不需要任何这样的界面。
请注意,您不是“强加”与纯虚函数类似的界面,而是提供界面。由于一切都在编译时得到解决,“强加”并不是一个强烈的要求。
答案 1 :(得分:2)
在替换代码中,您无法“多态”在foo
上拨打base<T>
。
答案 2 :(得分:2)
然而,似乎派生类不需要编译定义,因为它继承了一个(代码编译得很好而没有定义 my_type :: foo)。
C ++是懒惰的:如果你实际上没有使用它,它不会尝试创建base&lt; my_type&gt; :: foo()。 但是如果你尝试使用它,那么它将被创建,如果失败,编译错误将会流动。 但在你的情况下,base&lt; my_type&gt; :: foo()可以很好地实现:
template <class Derived>
struct base {
void foo() {
static_cast<Derived *>(this)->foo();
};
};
struct my_type : base<my_type> {};
void func() {
my_type m;
static_cast<base<my_type>& >(m).foo();
}
编译就好了。当编译器出现时 static_cast(this) - &gt; foo(),它将尝试查找my_type中可访问的foo()。还有一个:它叫做base&lt; my_type&gt; :: foo(),它是公开继承的类的公共。所以base&lt; my_type&gt; :: foo()调用base&lt; my_type&gt; :: foo(),你得到一个无限递归。
答案 3 :(得分:0)
不,想象以下情况:
template <typename T>
void bar(base<T> obj) {
obj.foo();
}
base<my_type> my_obj;
bar(my_obj);
将调用Base的foo而不是my_type的...
这样做,你会得到你的错误消息:
template <class Derived>
struct base {
void foo() {
sizeof(Derived::foo);
static_cast<Derived *>(this)->foo();
};
};
但我必须承认,我不确定这在GCC以外的编译器中是如何工作的,只能用GCC进行测试。