CRTP避免虚拟成员函数开销

时间:2011-07-20 18:47:26

标签: c++ templates virtual crtp

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
}

4 个答案:

答案 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进行测试。