2009-03-06 9 views
7

仮想関数とC++継承メカニズムの使用とテンプレートの使用との違いとは何ですか?C++のコンセプトチェックと継承

可能なことがかなり重なっているようです。すなわち、どちらの手法でも多態性を達成することが可能であるように思われる。だから、いつ他のものを優先させるのが理にかなっていますか?

私がこれを引き起こす理由は、テンプレートのコンテナがあり、コンテナ自体に階層関係があるからです。私はこれらのコンテナを使用するアルゴリズムを記述したいと思います。また、一部のアルゴリズムでは、テンプレートタイプが特定のコンセプトを満たしていることを知ることで利益を得ることができます(たとえばComparable)。

したがって、私はコンテナが多態的に動作するようにします。もう1つは、アルゴリズムを正しく実装したい場合は、コンセプトを使用する必要があります。ジュニア開発者とは何ですか?

答えて

6

私は概念をメタインタフェースの一種と考えています。彼らは能力の後でタイプを分類します。次のC++バージョンはネイティブな概念を提供します。私はC++ 1xのコンセプトを見いだすまで、それはどういう意味で別々ではないタイプを一緒に置くことができるのか理解できませんでした。 Rangeインターフェイスがあるとします。あなたは2つの方法でそれをモデル化することができます。一つはサブタイプ関係です:もちろん

class Range { 
    virtual Iterator * begin() = 0; 
    virtual Iterator * end() = 0; 

    virtual size_t size() = 0; 
}; 

、それから派生するすべてのクラスは、レンジインターフェースを実装し、あなたの機能を使用することができます。しかし今、あなたはそれが限られているのを見ます。アレイはどうですか?それも範囲です!

T t[N]; 

begin() => t 
end() => t + size() 
size() => N 

悲しいことに、そのインターフェイスを実装するRangeクラスから配列を派生させることはできません。余分な方法が必要です(オーバーロード)。第三者のコンテナはどうですか?ライブラリのユーザーは、コンテナを関数とともに使用することができます。しかし、彼はコンテナの定義を変更することはできません。ここでは、概念がゲームに入って来る:

auto concept Range<typename T> { 
    typename iterator; 
    iterator T::begin(); 
    iterator T::end(); 
    size_t T::size(); 
} 

今、あなたはTは、適切なメンバ関数を持っている場合は満たすことができるいくつかのタイプのサポートされている操作について何かを言います。ライブラリでは、汎用関数を記述します。これは、あなたが、それが必要な操作をサポートしていとして、あらゆるタイプを受け入れることができます:

template<Range R> 
void assign(R const& r) { 
    ... iterate from r.begin() to r.end(). 
} 

それは代替の偉大なものです。 タイプは、コンセプトに準拠した請求書に適合し、いくつかのインターフェースを積極的に実装するタイプだけでなく、次のC++標準はさらに進んでいます:普通の配列(というコンセプトマップ)によって適合するContainerコンセプトを定義します。既存の標準コンテナです。

私がこれを引き起こす理由は、コンテナ自体に階層関係があるテンプレート化されたコンテナがあるからです。私はこれらのコンテナを使用するアルゴリズムを記述したいと思います。また、一部のアルゴリズムでは、テンプレートタイプが特定のコンセプトを満たしていることを知ることで利益を得ることができます(たとえばComparable)。

実際には両方のテンプレートを使用できます。階層関係を維持してコードを共有し、アルゴリズムを一般的な方法で書くことができます。たとえば、コンテナが同等であることを伝えるためです。これは、標準的なランダムアクセスのようなものだ/前進/出力/入力イテレータカテゴリが実装されています:

// tag types for the comparator cagetory 
struct not_comparable { }; 
struct basic_comparable : not_comparable { }; 

template<typename T> 
class MyVector : public BasicContainer<T> { 
    typedef basic_comparable comparator_kind; 
}; 

/* Container concept */ 
T::comparator_kind: comparator category 

それは実際に、それを行うために合理的な簡単な方法です。これで、関数を呼び出すことができ、正しい実装に転送されます。

template<typename Container> 
void takesAdvantage(Container const& c) { 
    takesAdvantageOfCompare(c, typename Container::comparator_kind()); 
} 

// implementation for basic_comparable containers 
template<typename Container> 
void takesAdvantage(Container const& c, basic_comparable) { 
    ... 
} 

// implementation for not_comparable containers 
template<typename Container> 
void takesAdvantage(Container const& c, not_comparable) { 
    ... 
} 

実際には、これを実装するために使用できる手法があります。別の方法は、boost::enable_ifを使用して、毎回異なる実装を有効または無効にすることです。コンパイル時との差の簡単な例として

+0

C++ 1x?これは、彼らがこの10年間で新しい標準を発表することをあきらめたことを意味するのでしょうか、あるいは将来のC++開発について話していますか? – jpalecek

+0

http://www.research.att.com/~bs/C++0xFAQ.html#concepts – jmucchiello

+0

jpalecek、彼らは2010年にそれをリリースしたいと思います。私はそれをC++ 1xと呼ぶ習慣があります:) –

0

コンパイル時に決定できる場合は、テンプレートを使用します。それ以外の場合は、継承と仮想関数を使用します。

1

はい、両方のメカニズムで多態性が可能です。実際には、どちらもと呼ばれ、多型です。

仮想関数は動的なポリモーフィズムを提供します(実行時に決定されるため)。テンプレートは静的多形性を提供します(すべてはコンパイル時に決定されます)。

これはどちらの方が好きなのかという質問に答える必要があります。可能であれば、コンパイル時に作業を移動することをお勧めします。ですから、それを取り除くには、テンプレートを使用して多型性のニーズを解決してください。そして、それが不可能な場合(コンパイル時に正確な型がわからないため、実行時の型情報を使用する必要があるため)、動的多型に戻ります。

(もちろん、他の理由があるかもしれませんが、特に、テンプレートには多くのコードをヘッダーファイルに移動する必要がありますが、これは問題の可能性がありますが、これはまた、または問題であってもなくてもよい。)各「コンテナ」タイプは、各テンプレートタイプの固有なので、あなたが

template<typename T> 
class ContainerBase{}; 

template<typename T> 
class ContainerDerived : public ContainerBase<T> {}; 

ような何かを行うことができます。この特定のケースで

0

、の理由のメンバ関数がありません各コンテナ型はテンプレート型の特性に特化できませんでした。

0

と実行時に多型は、次のコードを考えてみます。

template<typename tType> 
struct compileTimePolymorphism 
{ }; 

// compile time polymorphism, 
// you can describe a behavior on some object type 
// through the template, but you cannot interchange 
// the templates 
compileTimePolymorphism<int> l_intTemplate; 
compileTimePolymorphism<float> l_floatTemplate; 
compileTimePolymorphism *l_templatePointer; // ???? impossible 

struct A {}; 
struct B : public A{}; 
struct C : public A{}; 

// runtime polymorphism 
// you can interchange objects of different type 
// by treating them like the parent 
B l_B; 
C l_C: 
A *l_A = &l_B; 
l_A = &l_C; 

コンパイル時の多型は、1つのオブジェクトの振る舞いが他に依存したときに優れたソリューションですオブジェクト。実行時多形性は、オブジェクトの振る舞いを変更する必要がある場合に必要です。

2が多型であるテンプレートを定義することによって組み合わせることができます

template<typename tType> 
struct myContainer : public tType 
{}; 

の質問は、あなたのコンテナの動作は(ランタイム多型)を変更する必要がある場合で、かつ行動は、オブジェクトに依存しますそれは(コンパイル時の多型)を含んでいます。