关于c ++:CRTP避免虚拟成员函数开销

CRTP to avoid virtual member function overhead

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)。实际上,如果提供了函数,则在使用派生类时不会调用基函数。

所以问题是,以下代码替换是否可以接受(和标准?):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 base &,并且您的接口由base中的(非虚拟)函数定义。如果您没有想要定义的接口(正如您在问题的第二部分中所建议的那样),那么首先就不需要这样做。

请注意,您不是像使用纯虚拟函数那样"强制"一个接口,而是提供一个接口。因为所有的问题都是在编译时解决的,"强制"并不是一个很强的要求。


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

C++是懒惰的:如果你不实际使用它,它就不会尝试创建Base[MyOyType >::FoE()。但是,如果您尝试使用它,那么它将被创建,如果失败,编译错误将流动。但在您的情况下,可以很好地通知base::foo():

1
2
3
4
5
6
7
8
9
10
11
12
13
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(),它将尝试查找在我的_类型中可访问的foo()。还有一个:它被称为base::foo(),它是公共继承类中的公共类。所以base::foo()调用base::foo(),得到无限递归。


在替换代码中,不能"多态"地调用base上的foo


不,想象一下以下情况:

1
2
3
4
5
6
7
template <typename T>
void bar(base<T> obj) {
   obj.foo();
}

base<my_type> my_obj;
bar(my_obj);

基地的foo将被调用,而不是我的类型的…

这样做,你就会得到你的错误信息:

1
2
3
4
5
6
7
template <class Derived>
struct base {
  void foo() {
    sizeof(Derived::foo);
    static_cast<Derived *>(this)->foo();
  };
};

但我必须承认,我不确定这在GCC之外的编译器中是如何工作的,只在GCC中测试。