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
#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は、非特定型の関数ポインターへのポインターを格納できます。たとえばA*、最初の引数として明示的に取る「静的」ポインターです。C ++ 11では、メンバー関数からこれらの関数を自動構築できます。C ++ 03ではstd::bind、メンバー関数ポインターを、最初の引数としてDをとる関数に変換できるようにする必要があります。A*

于 2013-03-22T18:39:30.560 に答える
1

以下のコードでこれを行うことができます [...] しかし、これはできません:

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

ここでの問題は、それBが class templateであり、それをインスタンス化していないことです。ポリモーフィズムや vtable とはあまり関係ありません。クラス テンプレートは、型に引数を渡すことによって型をインスタンス化するための単なる設計図 (実際にはテンプレート) ですが、それ自体は型ではありません。

インスタンス化するときは、いくつかのテンプレート引数を使用する必要がありますB。例えば:

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

そして、あなたはこれが呼び出しているのを見るはずB::Print()です. これが実際のです。

于 2013-03-22T17:02:14.123 に答える
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 に答える