まあ!それは本当に難しい宿題です。しかし、それはまた、働き、学ぶことは非常に良い問題です。
私はこれに答える最善の方法は、単純なユースケースから始め、段階的にソリューションを構築することだと思います。例えば
、あなたはで動作するようにstd::vector<int>
を、次のしていることとします
std::vector<int> vec;
vec.push_back(4);
vec.push_back(-8);
vec.push_back(1);
vec.push_back(0);
vec.push_back(7);
あなたは明らか以下のユースケース許可することをお勧めします:
std::for_each(vec.cbegin(), vec.cend(), _1);
をしかし、これを許可する方法?まず、_1
を定義する必要があります。次に、_1
の種類の関数呼び出し演算子の「何か」のオーバーロードを実装する必要があります。
Boost LambdaとBoost Bindでプレースホルダオブジェクト_1
、_2
を定義する方法は、ダミータイプにすることです。例えば、_1
オブジェクトは、タイプplaceholder1_t
を持っているかもしれません:「ダミーのタイプは」頻繁に、非公式にタグタイプと呼ばれているこのような
struct placeholder1_t { };
placeholder1_t _1;
struct placeholder2_t { };
placeholder2_t _2;
。多くのC++ライブラリ、実際にはタグタイプに依存するSTL(例:std::nothrow_t
)があります。これらは、実行するために「正しい」関数のオーバーロードを選択するために使用されます。本質的に、ダミーオブジェクトはタグタイプを持って作成され、これらは関数に渡されます。関数は何らかの方法でダミーオブジェクトを使用しません(実際にはほとんどの場合、パラメータ名が指定されていません)が、その余分なパラメータが存在するため、コンパイラは適切なオーバーロードを呼び出して呼び出すことができます。
placeholder1_t
の定義を、関数呼び出し演算子のオーバーロードを追加して拡張しましょう。我々はそれが何かを受け入れるようにしたいということを忘れないでください、その関数呼び出し演算子のオーバーロードは、自分自身を引数の型にテンプレート化されます。
struct placeholder1_t
{
template <typename ArgT>
ArgT& operator()(ArgT& arg) const {
return arg;
}
template <typename ArgT>
const ArgT& operator()(const ArgT& arg) const {
return arg;
}
};
それです!最も簡単なユースケースはコンパイルされて実行されます。
std::for_each(vec.cbegin(), vec.cend(), _1);
もちろん、基本的にはノーオペレーションになります。
_1 + 5
で作業しましょう。その式は何ですか。はですか?それは、(何らかの不明な型の)引数で呼び出されたときにその引数に5を加えた単項関数オブジェクトを返さなければなりません。これをより汎用的にすると、式は単体機能オブジェクト+
オブジェクトです。返されるオブジェクトは、単体の機能オブジェクトです。
返されるオブジェクトの型を定義する必要があります。単項機能タイプと単項機能の結果に追加されるオブジェクトのタイプ:それは、2つのテンプレートの型パラメータを持つテンプレートであろう
template <typename UnaryFnT, typename ObjT>
struct unary_plus_object_partfn_t;
「partfn」部分を表す機能的なタイプを指しバイナリ+
オペレータのアプリケーション。
template <typename UnaryFnT, typename ObjT>
struct unary_plus_object_partfn_t
{
UnaryFnT m_fn;
ObjT m_obj;
unary_plus_object_partfn_t(UnaryFnT fn, ObjT obj)
: m_fn(fn), m_obj(obj)
{
}
};
さて:このタイプのインスタンスは、単項機能オブジェクト(有するタイプUnaryFnT
)および他のオブジェクト(タイプObjT
を有する)のコピーを必要とします。関数呼び出し演算子はまた、任意の引数を許可するためにオーバーロードする必要があります。我々は、それが事前にあるのかわからないと式の型を参照するためにC++ 11 decltype
機能を使用します:
template <typename UnaryFnT, typename ObjT>
struct unary_plus_object_partfn_t
{
UnaryFnT m_fn;
ObjT m_obj;
unary_plus_object_partfn_t(UnaryFnT fn, ObjT obj)
: m_fn(fn), m_obj(obj)
{
}
template <typename ArgT>
auto operator()(ArgT& arg) const -> decltype(m_fn(arg) + m_obj) {
return m_fn(arg) + m_obj;
}
template <typename ArgT>
auto operator()(const ArgT& arg) const -> decltype(m_fn(arg) + m_obj) {
return m_fn(arg) + m_obj;
}
};
複雑に取得し始めていますが、何の驚きは、この中ではありませんコード。本質的には、関数呼び出し演算子は、実際にはあらゆる引数を受け入れるためにオーバーロードされていると言います。次に、引数にm_fn
(単項関数オブジェクト)を呼び出し、結果にm_obj
を追加します。戻り値の型はm_fn(arg) + m_obj
の宣言型です。
さて型が定義されていることを、我々は左のタイプplaceholder1_t
のオブジェクトを受け入れるバイナリ演算子+
の過負荷を書くことができます
template <typename ObjT>
inline unary_plus_object_partfn_t<placeholder1_t, ObjT> operator+(const placeholder1_t& fn, ObjT obj)
{
return unary_plus_object_partfn_t<placeholder1_t, ObjT>(fn, obj);
}
現在第2の使用ケースをコンパイルして実行することができます
std::transform(vec.cbegin(), vec.cend(), std::ostream_iterator<int>(std::cout, " "), _1 + 5);
std::cout << std::endl;
出力:
9 -3 6 5 12
を3210
これは基本的に問題を解決するために必要なすべてです。カスタム関数型を書く方法を考えてください。そのインスタンスは演算子のオーバーロードによって返される可能性があります。
EDIT:参照渡しを使用して関数呼び出し演算子のオーバーロードを改善しました。
EDIT2:場合によっては、オブジェクトのコピーではなくオブジェクトへの参照を格納する必要があります。たとえば、std::cout << _1
を収容するには、std::ios_base
コピーコンストラクタがプライベートであり、std::ostream
を含むstd::ios_base
から派生した任意のクラスのコンストラクトオブジェクトをコピーすることができないため、結果の機能オブジェクトにstd::cout
への参照を格納する必要があります。
std::cout << _1
を許可するには、ref_insert_unary_partfn_t
テンプレートを作成すると便利です。そのようなテンプレートは、上記unary_plus_object_partfn_t
の例のように、オブジェクトの種類と単項機能タイプにテンプレートされるであろう:このテンプレートのインスタンス化の
template <typename ObjT, typename UnaryFnT>
struct ref_insert_unary_partfn_t;
インスタンスとしてタイプObjT
のオブジェクトへの参照を格納する必要がありますほかのタイプUnaryFnT
の単項機能オブジェクトのコピーとして:
template <typename ObjT, typename UnaryFnT>
struct ref_insert_unary_partfn_t
{
ObjT& m_ref;
UnaryFnT m_fn;
ref_insert_unary_partfn_t(ObjT& ref, UnaryFnT fn)
: m_ref(ref), m_fn(fn)
{
}
};
挿入演算子、<<
のと同様に過負荷前と関数呼び出し演算子のオーバーロードを追加します。
std::cout << _1
の場合、返されるオブジェクトのタイプはref_insert_unary_partfn_t<std::basic_ostream<char>, placeholder1_t>
です。
Boost.Lambda ... – kennytm
@KennyTM:それがどのように機能するかを学ぶ理由。 –
テンプレートが必要な理由はわかりませんが、使用しない - 、*、/以外のすべての演算子をオーバーロードする必要があります。 –