std::vector<T>::iteratorが引数の関数をつくる

# はじめに
テンプレート引数`T`を具体化せずに、`std::vector<T>::iterator`を引数に持つ関数をつくりたい。
具体的にはこんな関数を実装したい。

```cpp
template<typename T>
void f(typename std::vector<T>::iterator begin, typename std::vector<T>::iterator end)
{
    //...
}
```

しかし、これはコンパイルエラーになる。
色々と検索したら一般化した話は出てくるんだけど、C++難しい…ってなる話ばかりなので、上の目的に特化して解決策を書いてみる。

# 背景

外部ライブラリに、`void f(float* p, int n)`のように配列を生ポインタと長さを表す整数で受ける関数があり、`iterator`入れられたら便利だなぁってことでラッパー関数を実装しようとした時のこと。
STLのコンテナクラスには、ポインタで要素に連続アクセスできるもの(例: `std::vector<T>`)とできないもの(例: `std::deque<T>`)があるので、前者はそのままポインタでぶちこんで、後者はvector等にコピーした後ぶちこむ、というように処理を分けたかった。
前者は`std::array<T, N>`か`std::vector<T>`ぐらいなので、とりあえず使用頻度の高い`std::vector<T>`のみを考える。
めんどくさがりなので、`std::vector<T>`のテンプレート引数`T`を抽象化したまま扱いたかったんだけど、上記のような素直な書き方はコンパイルできませんでしたってお話。

# 実装
## コンパイルに失敗する原因

そもそもコンパイルに失敗する原因を明確にしておこう。
Wandboxでclang13.0.0を指定して下のコードを実行してエラーを確認する。

```cpp
#include <iostream>
#include <algorithm>
#include <vector>

template<typename T>
void f(typename std::vector<T>::iterator begin, typename std::vector<T>::iterator end)
{
    // ただvectorの中身を表示するだけ
    std::for_each(begin, end, [](const auto& x) { std::cout << x << " "; });
    std::cout << std::endl;
}

int main()
{
    std::vector<float> v = {1.f, 2.f, 3.f};
    f(v.begin(), v.end());
    
    return 0;
}
```

```
prog.cc:16:5: error: no matching function for call to 'f'
    f(v.begin(), v.end());
    ^
prog.cc:6:6: note: candidate template ignored: couldn't infer template argument 'T'
void f(typename std::vector<T>::iterator begin, typename std::vector<T>::iterator end)
     ^
1 error generated.
```

テンプレート引数`T`を推測できないらしい。
調べると::の左側にあるテンプレート引数は推測できないことがわかった[^1]。言語仕様なので、仕方がない。

テンプレート引数`T`のところに`float`等の具体的な型を入れてオーバーロードする手もあるが、同じ処理するならなるべくまとめたい。

## 解決策1

`std::vector<T>`のテンプレート引数`T`が推測できないなら、推測できる形に変形すればいい。
具体的には、以下のように`iterator`自体をテンプレート引数とした関数で受け取り、そこから`std::vector<T>`のテンプレート引数`T`を推測してから、元の関数に入れてやる。

```cpp
#include <iostream>
#include <algorithm>
#include <vector>

template<typename T>
void f(typename std::vector<T>::iterator begin, typename std::vector<T>::iterator end)
{
    // ただvectorの中身を表示するだけ
    std::cout << "vector specialized" << std::endl;
    std::for_each(begin, end, [](const auto& x) { std::cout << x << " "; });
    std::cout << std::endl;
}

template<typename Iterator, typename T = typename Iterator::value_type>
void g(Iterator begin, Iterator end)
{
    f<T>(begin, end);
}

int main()
{
    std::vector<float> v = {1.f, 2.f, 3.f};
    g(v.begin(), v.end());

    return 0;
}
```

これは無事にコンパイルできるし、正常に動作する。
これで、テンプレート引数Tを具体化せずに、std::vector<T>::iteratorを引数に持つ関数をつくることができました。
めでたしめでたし。

</br>
</br>
</br>

…と言いたいところだが、当初の目的はSTLのコンテナクラスのうち、ポインタで要素に連続アクセスできるもの(例: `std::vector<T>`)とできないもの(例: `std::deque<T>`)のそれぞれに対して、`iterator`を受け取る関数を別々に定義したい、というところだった。

上記の解決策1では、`value_type`を持つ型であれば全て関数`g`を通るため、テンプレート引数`Iterator`が`std::vector<T>::iterator`なのか、`std::deque<T>::iterator`なのかは関数`f`で分けることになる。
`std::vector<T>`のiteratorに対してのみ適用される形で関数`f`を書いているが、実際には`std::deque<T>`や`std::set<T>`に対してもそれぞれ関数`f`をオーバーロードして書かなければならず、めんどくさい。
`std::vector<T>`以外の`iterator`を受け取る関数をテンプレートでまとめようとすると、今度は関数`g`の中身もめんどくさいことになる。

## 解決策2

結局、要素型`T`を受け取りつつ、任意の`iterator`をテンプレートで受け取れるような形をつくってあげる。
具体的には、先ほどの関数`g`をクラステンプレートの静的メンバー関数にしてあげればよい。

```cpp
#include <iostream>
#include <algorithm>
#include <vector>
#include <deque>

template<typename T>
void print(const T* p, int n)
{
    // ただ中身を表示するだけ
    std::for_each(p, p + n, [](const auto& x) { std::cout << x << " "; });
    std::cout << std::endl;
}

template<typename T>
class Foo
{
public:
    // vector専用
    // 実際には std::vector<T>::const_iterator も考慮する必要がある
    static void f(typename std::vector<T>::iterator begin, typename std::vector<T>::iterator end)
    {
        std::cout << "vector specialized" << std::endl;
        print(&(*begin), static_cast<int>(end - begin)); // vectorは生ポによる要素の連続アクセス可能
    }
    
    // 抽象化したやつ
    template<typename Iterator>
    static void f(Iterator begin, Iterator end)
    {
        std::cout << "generalized" << std::endl;
        std::vector<T> tmp(begin, end); // vectorにコピーして生ポ扱えるようにしてからぶちこむ
        print(tmp.data(), static_cast<int>(tmp.size()));
    }
};

template<typename Iterator, typename T = typename Iterator::value_type>
void g(Iterator begin, Iterator end)
{
    Foo<T>::f(begin, end);
}

int main()
{
    std::vector<float> v = {1.f, 2.f, 3.f};
    std::deque<float> d = {1.f, 2.f, 3.f};
    
    std::cout << "=====vector=====" << std::endl;
    g(v.begin(), v.end());
    
    std::cout << "=====deque=====" << std::endl;
    g(d.begin(), d.end());  
    
    return 0;
}
```

これを実行すると、以下のように、`std::vector<T>`に対しては専用のもの、`std::deque<T>`に対してはテンプレート引数`Iterator`で抽象化したものが呼び出されていることがわかる。

```
=====vector=====
vector specialized
1 2 3 
=====deque=====
generalized
1 2 3
```

これで、`std::vector<T>`と、ポインタで要素に連続アクセスできないもの(例: `std::deque<T>`)のそれぞれに対し、`iterator`を受け取る関数を定義できました。めでたしめでたし。

# おわりに
こんな形で実装しましたが、もっと簡単にできるよ！って方法あったら教えてください。
C++難しい… 1ミリもわからん…

# 余談
今回、STLのコンテナクラスのうち、ポインタで要素に連続アクセスできるものとして`std::vector<T>`のみを扱ったが、実際には固定長配列の`std::array<T, N>`もある。
これも同様に`std::array<T, N>::iterator`を引数に持つ関数を作ろうとするんだけど、配列長を表すテンプレート引数`N`は`iterator`から取得できない…
そもそも`std::array<T, N>`の`iterator`は完全に実装依存なので、例えばmsvcでは`_Array_Iterator<T, N>`というクラスなんだけど、gccでは`T*`のように生ポインタで定義されているので、後者の場合はどうやってもテンプレート引数`N`は推測できない…

SFINAEでごにょごにょすれば出来るかもしれんけど、進んで沼には入りたくないよねぇ…


[^1]: [C++関数テンプレートと半順序とオーバーロード](https://qiita.com/_EnumHack/items/cd904d383588ddb2189f)