2

我有一种情况,我需要在没有 vtable 的情况下实现多态性。这是我想要做的

  • 有一个类层次结构:C 扩展 B,B 扩展 A
  • 这个想法是在A中声明一个函数指针,B和C的构造函数将它们对应的方法分配给A中的函数指针
  • 使用下面的代码,我能够实现 C 类的多态性,但不能实现 B 类的多态性。

显然我在这里遗漏了一些东西。我不确定这是否可能。非常感谢对此问题的任何见解。

我可以用下面的代码做到这一点

A<C> *c = new C();
c->BasePrint(); //Reached C's Print

但不是这个

// A<B> *b = new B();
// b->BasePrint(); //Intentionally incorrect to demonstrate the problem.

有什么办法可以做到这一点?

template <typename T>
class A
{
public:
    typedef void (T::*PrintFn)(void);
protected:
    PrintFn printFn;
public:
    void BasePrint()
    {
        if(printFn)
            (((T*)this)->*printFn)();
    }
};


template <typename T>
class B : public A<T>
{
public:
    B()
    {
        printFn = &B::Print;
    }

    void Print()
    {
        //Print B
    }
};



class C : public B<C>
{
public:
    C()
    {
        printFn = &C::Print;
    }

    void Print()
    {
        //Print C
    }
};
4

3 回答 3

1

您没有为 B 指定模板参数:

A<B> *b = new B();

与其声明相反:

template <typename T>
class B : public A<T>

您应该使用以下几行内容:

A<B<X>> *b = new B<X>();

X作为非模板类​​型。

于 2013-03-22T17:01:45.943 回答
1

我可以用下面的代码[...]做到这一点,但不是这个:

    A<B> *b = new B();
    b->BasePrint(); //Intentionally incorrect to demonstrate the problem.

好吧,这里的问题是这B是一个类模板,而您没有实例化它。它与多态性或 vtable 没有太大关系。类模板只是通过向类型传递参数来实例化类型的蓝图(实际上是模板),但它本身不是类型。

实例化时应该使用一些模板参数B。例如:

A<C>* b = new B<C>();
b->BasePrint();

你应该看到这个调用B::Print()。这是一个活生生的例子

于 2013-03-22T17:02:14.123 回答
1
#include <iostream>
#include <typeinfo>

struct own_type {};

template<template<typename T>class CRTP, typename In, typename D>
struct DoCRTP: CRTP<In> {};
template<template<typename T>class CRTP, typename D>
struct DoCRTP<CRTP, own_type, D>: CRTP<D> {};

template<typename D>
struct A {
   D* self() { return static_cast<D*>(this); }
   D const* self() const { return static_cast<D*>(this); }
   A() {
      std::cout << "A<" << typeid(D).name() << ">\n";
      self()->print();
   }
};

template<typename T=own_type>
struct B:DoCRTP<A, T, B<T>> {
   B() {
      std::cout << "B<" << typeid(T).name() << ">\n";
   }
   void print() { std::cout<<"I am a B\n"; }
};

template<typename T=own_type>
struct C:DoCRTP<B, T, C<T>> {
   C() {
      std::cout << "C<" << typeid(T).name() << ">\n";
   }
   void print() { std::cout<<"I am a C\n"; }
};

void test() {
   std::cout << "Instance of B<>:\n";
   B<> b;
   std::cout << "Instance of C<>:\n";
   C<> c;
}

int main() {
   test();
}

在这里,我们有一种方法可以传入最派生的类,如果不传入任何内容,则假定您是最派生的类。

然而,你的设计有一个问题——A已经完全知道它的类型情况,所以不需要虚拟行为! BasePrint可以static_cast<T*>(this)->Print(),你会取消你的开销。

您遇到的基本问题是您将特定类型的成员函数指针存储在您的基类A中。

无模板A可以存储指向非特定类型函数指针的指针——比如显式将 aA*作为第一个参数的“静态”指针。在 C++11 中,您可以从成员函数中自动构建这些函数。在 C++03 中,std::bind应该让您将成员函数指针转换为D以 aA*作为第一个参数的函数。

于 2013-03-22T18:39:30.560 回答