2013-06-07 21 views
9

私は問題の正確な説明がないので、これが可能かどうかを尋ねています(もしそうであれば、他の情報も素晴らしいでしょう)。C++ - 仮想関数の代わりにテンプレートを使用

class Derived : public Base<Derived> 
{ 
    ... 
}; 

が、私はこの最後の通路を理解していなかった。

プログラマは1つが、これに類似した方法でテンプレートを使用することができ、原因仮想関数多型に実行時のオーバーヘッドで負担していないことを教えてくれました。どのようにして、テンプレートを使って通常の仮想関数の多形性を置き換えることができますか?私は間違っていたのですか?

+2

ひとつはできません。 2つは全く異なっており、さまざまな問題を解決します。しかし、一般的に、人々がYを使用すべきときにXを誤って使用した場合、「XではなくYを使用する」ことがアドバイスになります。それは、XとYが関連しているという意味ではなく、人々が何をしているのか分からないということだけです。 –

+7

Whoa、[CRTP](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)パターンです。 – maverik

答えて

17

具体的には、仮想関数を持つ基本クラスの代わりにCRTPを使用して、仮想関数呼び出しオーバーヘッドなしでtemplate method patternを実装することができます。仮想関数を持つ

は、TMPは、次のようになります。

class ProvidesMethod { 
protected: 
    void Method() { 
    // do something 
    Part1(); 
    // do something else 
    Part2(); 
    // do something final 
    } 

private: 
    virtual void Part1() = 0; 
    virtual void Part2() = 0; 
}; 

class ExposesMethod : private ProvidesMethod { 
public: 
    using ProvidesMethod::Method; 

private: 
    void Part1() { 
    // first part implementation 
    } 
    void Part2() { 
    // second part implementation 
    } 
}; 

をCRTPで、それは次のようになります。

template <typename Derived> 
class ProvidesMethod { 
protected: 
    void Method() { 
    // do something 
    self().Part1(); 
    // do something else 
    self().Part2(); 
    // do something final 
    } 

private: 
    Derived& self() { return *static_cast<Derived*>(this); } 
    const Derived& self() const { return *static_cast<const Derived*>(this); } 
}; 

class ExposesMethod : private ProvidesMethod<ExposesMethod> { 
public: 
    using ProvidesMethod<ExposesMethod>::Method; 

private: 
    friend class ProvidesMethod<ExposesMethod>; 
    void Part1() { 
    // first part implementation 
    } 
    void Part2() { 
    // second part implementation 
    } 
}; 
+0

「自己」メソッドが必要な理由は何ですか? Part()を呼び出すだけではどうですか? ? – paulm

+0

これは、 'Method'の文脈で' This'の型を使用するでしょう。これは 'ProvidesMethod'です。これは* Part1'を持っていません - これを試してみるとコンパイラはすぐに指摘するでしょう変化する。代わりに、CRTPベースにパーツのデフォルト実装がある場合、コンパイラはそれらを使用して、仮想呼び出しではないため、派生クラスがそれらをオーバーライドすることはありません。 –

+0

ありがとう、分かり次第です:) – paulm

2

テンプレートを使用して仮想関数のようなものを提供する方法がわかりません - 私には奇妙に思えますが、最終的にコンパイラを使用せずに独自のバージョンの仮想関数を実装するというトリッキーなことはありません関数を呼び出すコードがオブジェクトの型がわからず、そのオブジェクト型に対して正しい関数を呼び出す方法を作る方法を知ることは非常に難しいです。どの仮想関数が何をしているのですか?

さらに、個人的な経験から、仮想関数のACTUALオーバーヘッドは非常に小さく、非常に極端な場合にのみ違いがあります(非仮想関数がインライン化される主なケースであり、関数を呼び出すオーバーヘッドは、実行時間全体のかなりの部分を占めています(これは、関数を何度も呼び出す必要があることを意味します)。 "何をすべきか" (if文やそれに似たものを使って)「仮想関数」のバリエーションを実装する以外にも、それはホイールの再開発だけであり、新しいホイールが既存のものより優れていない限り、それは良いことではありませんアイディア]

+0

私は、CRTPが実際の生活習慣にほとんど影響を与えないようにするために、あまりにも時間がかかることに同意します。しかし、コードをインライン化する機能は、短時間で実行しなければならない重要な機能です。これは、関数呼び出しのオーバーヘッド、スタックの使用を回避し、キャッシングを改善することができます。仮想関数に必要な間接参照は重要です。 – seand

+0

私は同意しますが、実際にどのクラスに基づいて異なるものを実行する必要がある場合は、 'if'、' switch'、または 'function pointer'型の解決策が必要です。バーチャルは、ほとんどの場合、それらの中で最も効率的です。ルックアップは、vtableがキャッシュになく、 'this'ポインタがキャッシュにない場合にのみ重要です。また、一部のメンバーデータを使用している場合は、少なくとも' this'ポインタをキャッシュにロードする必要があります。 –

+0

@MatsPetersson選択文は必要ありません。 CRTPは以下のように仮想ディスパッチを "シミュレート"します: 'template class Base {void foo(){((Derived *)this)}> pseudoVirtualBar(); } 'これは、テンプレートパラメータ(派生クラス)に基づいて異なる関数を呼び出します。 – Angew

3

これはCRTP (Curiously Recurring Template Patternのために)あなたがそれを見ることができるように。

私は本当にそれは古典的な多型に取って代わる可能性がどのように表示されていないが...

一方、1は、いくつかのケースでは(のためのポリシーベースの設計を参照してください、テンプレートによってクラスの複雑な階層構造を置き換えることができます詳細はこちら)、いつでも可能なわけではありません。

3

Julienが指摘したように、これはCRTPです。あなたはそれを見るべきです。 しかし、CRTPは仮想関数を置き換えることはできません。あなたのために特別な場合には、実際には最初に仮想関数が必要なわけではありません。テンプレートは、コンパイル時に多態性を提供します。 仮想関数は実行時の多形性を提供します。コンパイル時にどのオーバーライドが呼び出されるのかわからない場合、テンプレートベースのソリューションは動作しません。実行時にオブジェクトの実際のタイプが何になるかを常に知っているならば、仮想関数は必要ありません。