動的ポリモーフィズムはオブジェクトには適切に機能しますが、アルゴリズムには非常にうまく機能しません。アルゴリズムの場合、静的ポリモーフィズムを使用する方がはるかに優れています。それらの間をサポートするコンテナーのシステムが必要な場合は、operator+()
それらが何らかの形で名前空間を参照していることを確認し、この名前空間で適切なものを定義してくださいoperator+()
。たとえば、名前空間内の何かから演算子を継承std::vector<T, A>
するアロケータで使用できます。A
以下は、このアプローチの例です。
基本的には、名前空間に適切な演算子を実装するという考え方です。たとえば、をaddable
実装operator+()
してprintable
実装しoperator<<()
ます。これらの名前空間のどちらも、タイプが何らかの形でそれらを参照しない限り、たとえば、この名前空間のタイプから継承するか、これらのタイプの1つを継承するタイプのテンプレート引数を使用することによって調べられません。このルックメカニズムは、引数依存ルックアップと呼ばれます。したがって、これらの名前空間は両方とも、struct tag {};
継承時に費用がかからない空を提供します。
これらの名前空間を何らかの形で参照するコンテナを取得するために、以下のコードは、これらのタイプの両方から派生するアロケータクラステンプレートを作成するだけです。次に、このアロケータをとで使用しstd::vector<T, A>
てstd::list<T, A>
タイプエイリアスを作成し、対応するコンテナを簡単に作成します。すべてがうまく機能していることを示すmain()
には、演算子の使用法を示します。
以下のコードは、表記を少し短くするために、いくつかの場所でC++2011を利用しています。この原則は、C++2003でも機能します。主に、型エイリアスと初期化子リストの使用は機能しません。
#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <list>
#include <vector>
namespace addable
{
struct tag {};
template <typename T0, typename T1>
T0 operator+ (T0 const& c0, T1 const& c1)
{
T0 rc;
std::transform(c0.begin(), c0.end(),
c1.begin(),
std::back_inserter(rc),
std::plus<typename T0::value_type>());
return rc;
}
}
namespace printable
{
struct tag {};
template <typename T, typename = typename T::value_type>
std::ostream&
operator<< (std::ostream& out, T const& value)
{
out << "[";
if (!value.empty()) {
std::copy(value.begin(), value.end() - 1,
std::ostream_iterator<typename T::value_type>(out, ", "));
out << value.back();
}
return out << "]";
}
}
template <typename T>
struct my_allocator
: addable::tag
, printable::tag
, std::allocator<T>
{
};
template <typename T>
using my_vector = std::vector<T, my_allocator<T>>;
template <typename T>
using my_list = std::vector<T, my_allocator<T>>;
int main()
{
my_vector<int> v({ 1, 2, 3, 4 });
my_list<int> l({ 2, 3, 4, 5 });
my_vector<int> rc = v + l;
std::cout << v << " + " << l << " = " << (v + l) << "\n";
}