【初心者C++er Advent Calendar 2016 8日目】関数テンプレートと関数オブジェクト
タイトルは釣りです。
初心者C++er Advent Calendar 2016 8日目の記事になります。
8日が空いていたので簡単なものを書きます。
前回の記事を書いたあとに
(省略されたtemplateのが気になった)
— Sd2E 002/100 (@chiwawa_star) 2016年12月6日
と、言われたので今回はその捕捉になります。
[関数テンプレート]
前回『残念ながら関数の引数型で auto を使用して型推論を行うことは出来ません』と書きました。
確かに auto
を使用して型推論を行うことは出来ませんが、template
を使用して型推論を行うことは出来ます。
template<typename A, typename B> auto plus(A a, B b){ return a + b; } plus(1, 2); // int + int = int plus(1, 3.14f); // int + float = float plus(std::string("homu"), std::string("mami")); // std::string + std::string = std::string
こんな感じに auto
ではなくて template
を使用して引数型を推論する形になります。
基本的な挙動はラムダ式と同じになります。
[関数テンプレートの問題点]
これだけじゃ味気ないのでもうちょっと突っ込んで関数テンプレートの場合の問題点を上げます。
さて、次のように std::for_each
とラムダ式を利用して値を出力することが出来ます。
auto output = [](auto x){ std::cout << x << std::endl; }; std::vector<int> xs = {1, 2, 3, 4, 5}; std::for_each(std::begin(xs), std::end(xs), output);
しかし、これを関数テンプレートで行おうとするとエラーになります。
template<typename T> void output(T t){ std::cout << t << std::endl; } std::vector<int> xs = {1, 2, 3, 4, 5}; // error: no matching function for call to 'for_each' std::for_each(std::begin(xs), std::end(xs), output);
これは関数テンプレートを値として使用する場合に『テンプレートの型を明示する』必要があるためです。
ですので、関数などに関数テンプレートを渡す場合は次のようにテンプレートの型を明示する必要があります。
template<typename T> void output(T t){ std::cout << t << std::endl; } std::vector<int> xs = {1, 2, 3, 4, 5}; // output<int> を明示する std::for_each(std::begin(xs), std::end(xs), output<int>);
これで解決します。
[関数オブジェクトを使用する]
さて、関数テンプレートのテンプレート型を明示化することで解決しましたが、毎回型を記述するのは手間です。
そこで、関数ではなくてクラスを利用して回避します。
// operator () を定義したクラスを定義する struct output_{ template<typename T> void operator ()(T t) const{ std::cout << t << std::endl; } }; // そのクラスのオブジェクトを用意する output_ const output; std::vector<int> xs = {1, 2, 3, 4, 5}; // あたかも関数のように振る舞うオブジェクト std::for_each(std::begin(xs), std::end(xs), output);
関数ではなくてメンバ関数として定義することで型を明示化しなくてもよいようになります。
また、このように operator ()
を定義して関数のように振る舞うオブジェクトのことを一般的に関数オブジェクトと呼びます。
[まとめ]
よく『template
は難しい』みたいなことを聞きますが、やってることは単なる型推論なだけなので auto
を理解してるのであれば template
に置き換えるだけなのでそんなに難しくはないと思います。
auto
にしてもそうですが template
を使用することでどんどん型による依存を減らして行くことが可能になります。