避免虚成员函数开销

CRTP to avoid virtual member function overhead

本文关键字:函数 开销 成员      更新时间:2023-10-16

在CRTP中,为了避免动态多态性,提出了以下解决方案,以避免虚成员函数的开销,并强加一个特定的接口:

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
}

这个模式的要点是,据我所知,您可以简单地作为template <typename T> base<T> &传递参数,并且您的接口由base<T>中的(非虚)函数定义。如果您没有想要定义的接口(正如您在问题的第二部分所建议的那样),那么首先就不需要这些。

请注意,您不是像使用纯虚函数那样"强加"一个接口,而是提供一个接口。由于一切都是在编译时解决的,所以"强加"并不是一个强烈的要求。

在您的替换代码中,您不能"多态"调用foobase<T>

然而,派生类似乎不需要定义来编译,因为它继承了一个 (代码编译良好,而不定义 my_type::foo)。

c++是懒惰的:如果你不实际使用base::foo(),它不会尝试创建它。但是如果您尝试使用它,就会创建它,如果失败,就会出现编译错误。但是在您的情况下,可以很好地实例化base::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)->foo(),它将尝试找到一个可以在my_type中访问的foo()。有一个:它被称为base::foo(),它是从一个公开继承的类中公开的。所以base::foo()调用base::foo(),你就得到了一个无限递归

不,想象一下以下情况:

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