4

CRTPの理解を深めようとしています。これまでのところ、次のような関数を書くことができるというのが私の理解です。

template <class T>
void foo(Base<T> x ) { x.do_stuff() }

xここで、 function に渡される実際のコンパイル時の派生オブジェクトに応じて、foo()さまざまなことを行います。

ただし、クラスを派生DerivedBasedo_stuff()、非仮想であるがオーバーライドされDerived::do_stuffた . したがって、CRTPを使用するのが正確な場合は、シャドーイング/マスキングに対するCRTPの利点を示す最も単純で重要な例です。

4

1 に答える 1

6

CRTP のポイントは、派生オブジェクトの型を仮想性なしで取得できるようにすることです。もしあなたがそうするなら

struct B { void foo() const; }
struct D : B { void foo() const; }

void bar(const B& x) { x.foo(); }

仮想関数ではないため、オブジェクトを渡すときではなく呼び出しbarます。呼び出されたい場合は、仮想機能または CRTPが必要です。B::fooD::fooDfooD::foo

最も単純な CRTP の場合:

template <typename>
struct B { void foo() const; }

struct D : B<D> { void foo() const; }

template <typename T>
void bar(const B<T>& x)
{
    static_cast<const T&>(x).foo();
}

これは、オブジェクトD::foo()に渡すときに呼び出されます。barD

ただし、代わりの CRTP トリックは、実装を提供することを強制しDます。foo

template <typename T>
struct B
{
    void foo() const { static_cast<const T*>(this)->foo_impl(); }
    // default implementation if needed
    // void foo_impl() const { ... }
};

struct D : B<D> { void foo_impl() const { ... } };

template <typename T>
void bar(const B<T>& x) { x.foo(); }

Bただし、 (foo正しくディスパッチされるように)のテンプレート パラメータと、テンプレートbar関数が必要です。

また、CRTP を使用しない場合は、仮想デストラクタを使用することをお勧めします。これにより、完全にインライン化される軽量クラスに不要なオーバーヘッドが追加される可能性があります。friend TCRTP を使用すると、単純に保護されたデストラクタ ( C++0x ではプライベート デストラクタ + ) を記述できます。

于 2011-08-30T07:16:05.033 に答える