テンプレートを使用してジェネリック プログラミングを行う場合は、インターフェイスに対してコーディングする必要があります。ここでは、この用語の OOP の意味を使用していません。代わりに、より広い意味です。
例として、Random-Access Iterator の概念に似たインターフェイスに対してコーディングする関数テンプレートを次に示します。
template<typename It>
typename std::iterator_traits<It>::value_type
sum(It first, It last)
{
typedef typename std::iterator_traits<It>::difference_type diff_t;
diff_t const size = last - first;
typename std::iterator_traits<It>::value_type accum = 0;
for(diff_t i = 0; i != size; ++i) {
accum += first[i];
}
return accum;
}
(もちろん、この例は偽物です。ここでの意図は、複数のランダム アクセス イテレータ操作を示すことです。)
コントラクトでItRandom-Access Iterator を指定しているため、次のものにアクセスできることがわかっsumています。
- メンバー型
std::iterator_traits<It>::value_typeおよび、それぞれおよびstd::iterator_traits<It>::difference_typeの結果から初期化できる型ですoperator[]operator-
Itlikeoperator-およびの操作。双方向イテレータoperator[]などでは使用できません。
そのため、 、 、および とsum一緒に使用できます。これらはすべて異なるタイプであり、いくつかの側面で異なる場合がありますが、少なくともすべてランダム アクセス イテレータの概念のモデルです。std::vector<int>::iteratorstd::deque<double>::const_iteratorlong*
0(観察者は、and+=を使用することで、コントラクトで がvalue_type算術のような型であることを要求していることに気付くでしょう。これもまた、コーディング対象のインターフェイスです!)
Filters次に、明らかに として使用する予定の を設計するときは、 が満たす必要Filters<FilterLike>がある一般的な最小限のインターフェイスは何かを自問する必要がありますFilterLike。FilterXおそらくいくつかの操作を除いてほとんどのものがある場合はFilterLike、いくつかのオプションがあります。
あなたの質問で言及したように、Filtersその特定の操作を使用する場所はどこでも、FilterX特別に処理されるように特別なケースにすることができます-これはおそらくあなたができる最悪のことです。操作が必要なすべてのサイトで実行する必要があるという点で脆弱です (現在は 1 つだけのように見えても、将来はどうなるでしょうか?)。いいえ、クラステンプレート関数メンバーの関数本体内で型をオンにできないという点で不便です(冗長で明白でないさまざまな手法を使用する必要があります)。Filtersそして、知っておく必要がある結合を導入しますFilterX-なぜそれを気にする必要があるのでしょうか?
の明示的な特殊化を記述しFilters<FilterX>ます。FilterXこれは上記とよく似ていますが、1 つまたは 2 つの操作だけが異なる場合は興味深いオプションです。前のソリューションとは異なり、これはプライマリ テンプレートがそのまま残され、FilterX特定のものはすべて同じ場所に配置されるため、脆弱ではありません。一方、 の半分がFilterX既に のように動作している場合は、 のコードの半分が重複しているか、 と の間の共通コードをリファクタリングするために追加の作業が必要でFilterあることを意味するに違いありません。したがって、結合の量はさまざまです。プライマリ テンプレートがこの明示的な特殊化について知る必要がない場合、それは適切なオプションであり、明示的な特殊化をプライマリ テンプレートにバンドルする必要さえありません。Filters<FilterLike>Filters<Filter>Filters<FilterX>
AdaptedFilterXインターフェイスのモデルであるan を作成し、FilterLikeそのすべての操作を基になる に転送しますFilterX。いくつかFilterXのがありFilterY、それらはすべて のほぼモデルですFilterが、共通のインターフェースを持っています。AdaptedFilter<FilterX>ある意味で、AdaptedFilterテンプレートは a のモデルを a のモデルに「変換」しますFilterXLike。FilterLike
余談ですが、C++11 を使用している場合は、 withFilterを構築するために任意の引数を受け入れるように記述できます。FilterLike
template<typename... Args>
void Filter(Args&&... args)
{
FilterLike filter(std::forward<Args>(args)...);
// go on using filter...
}
ただし、単に受け入れる方がおそらく簡単です(C ++ 03で機能します)FilterLike:
void Filter(FilterLike filter)
{
// go on using filter...
}