map 色々

http://ls-al.jp/blog2/?itemid=3477 より。色んな map を書いてみます。

template <typename F, typename A>
std::vector<typename std::result_of<F(A)>::type>
map(const F& f, const std::vector<A> &v)
{
  std::vector<typename std::result_of<F(A)>::type> r;
  for(auto it = v.begin(); it != v.end(); it++)
    r.push_back(f(*it));
  return r;
}

全然ジェネリックじゃないじゃないか!と思ったあなた…今日はそういう話ではないので、すみませんがここはグッと堪えてください。
std::result_of は type_traits ヘッダにある、関数オブジェクトと引数型から、適用後の返値型を求めるメタ述語(何て言うのが適切か分からない…)で、とても便利です。が、これを使わずに書くこともできます。
何度か書いてますけど、今のところ g++-4.6 ではこの std::result_of、functional ヘッダにあるので気をつけましょう。

template <typename F, typename A>
std::vector<decltype(fun(param))>
map_(const F& f, const std::vector<A> &v)

中身同じなのでシグネチャだけ。

decltype 指定子を使うことで、式を評価した結果の型を取得することができます。これを利用すれば、関数適用を書いてやれば関数の返値型が取得できる。問題は「関数」とその「引数」の値をどこから持ってくるか、なのですが…

decltype に書く式は、式なら基本的にはなんでもありです。なので、値は無理やり「ごまかして」しまいましょう。

template <typename T> T dummy();
template <typename F, typename A>
std::vector<decltype(dummy<F>()(dummy<A>()))>
map(const F& f, const std::vector<A> &v)

実はこの dummy 相当の、つまり「評価されないコンテキストで無理やり値をごまかす」時のための関数テンプレートが標準で用意されていて、それが std::declval です。これを使うと、あまり変わりませんが次のように書くことができます。

template <typename F, typename A>
std::vector<decltype(std::declval<F>()(std::declval<A>()))>
map(const F& f, const std::vector<A> &v)

std::declval は utility ヘッダにある…はずなんですが、これも g++-4.6 では何故か type_traits ヘッダで定義されています。
std::declval はジェネリックなコードを書く際には割と有用なので、覚えておく価値がそれなりにあるかもしれません。
とはいえこれでは汚いままでちょっと酷いですね。問題は返値型を書く段階ではまだ引数が見えていないことなのですが、解決することはできるでしょうか?
これは C++0x で導入された新しい形式の関数宣言を利用することで解決することができます。

template <typename F, typename A>
auto map(const F& f, const std::vector<A> &v) ->
std::vector<decltype(f(std::declval<A>()))>

これで関数オブジェクトに関しては何とかなりましたが、vector の要素型の値に関しては、存在しないのでうまくいきませんね。上の例では std::declval を利用していますが、もっとルーズに適当なメンバ関数を呼び出してしまうのも手でしょう。

template <typename F, typename A>
auto map(const F& f, const std::vector<A> &v) ->
std::vector<decltype(f(v.front()))>

評価されないので、空だろうが何だろうがお構い無しです。

というわけで、色んな map を書いてみました。おしまい。
ちなみにこれは 15 日の話とはまた別です。