テンプレートを使用してジェネリック プログラミングを行う場合は、インターフェイスに対してコーディングする必要があります。ここでは、この用語の 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;
}
(もちろん、この例は偽物です。ここでの意図は、複数のランダム アクセス イテレータ操作を示すことです。)
コントラクトでIt
Random-Access Iterator を指定しているため、次のものにアクセスできることがわかっsum
ています。
- メンバー型
std::iterator_traits<It>::value_type
および、それぞれおよびstd::iterator_traits<It>::difference_type
の結果から初期化できる型ですoperator[]
operator-
It
likeoperator-
およびの操作。双方向イテレータoperator[]
などでは使用できません。
そのため、 、 、および とsum
一緒に使用できます。これらはすべて異なるタイプであり、いくつかの側面で異なる場合がありますが、少なくともすべてランダム アクセス イテレータの概念のモデルです。std::vector<int>::iterator
std::deque<double>::const_iterator
long*
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...
}