cpp演算子のオーバーロードで遊ぶ

# Arrayクラスのイテレータクラスの再実装

https://cpp.rainy.me/025-array-iterator.html#%E3%82%A4%E3%83%86%E3%83%AC%E3%83%BC%E3%82%BF%E3%83%BC%E3%81%AE%E4%B8%AD%E8%BA%AB

上記サイトを参考にarrayクラスを再実装中。イテレータの再実装編にさしかかったので、まずは自力で実装してみることにした。とりあえず以下のように出来上がる。

```cpp
template <typename Array> 
struct Array_iterator{
    Array &a;
    size_t idx;
    Array_iterator(Array &a): a(a), idx(0){}
    typename Array::reference_type operator *(void){
        return a[idx];
    }
    Array_iterator operator ++(void){
        idx++;
        return *this;
    }
    Array_iterator operator --(void){
        idx--;
        return *this;
    }
};
```

# イテレータについて
std::begin()はArrayクラスのbegin()を呼び出す。その際にArray_iteratorクラスを返す。
Array_iteratorクラスは*演算子と++演算子と--演算子をオーバーロードしている。
*演算子ではある要素への参照を返し、++または--演算子ではある要素が何番目かを管理するメンバ変数idxを操作する。

# こんな書き方が出来てしまう
```cpp
int main(void){
    using Array_double5 = Array<double, 5>;
    Array_double5 a = {1.1, 2.2, 3.3, 4.4, 5.5}; 
    const Array_double5 b = {1.1, 2.2, 3.3, 4.4, 5.5}; 
    cout << *a.begin() << endl;
    cout << *(----------a.end()) << endl;
    cout << (*a.begin() == *(----------a.end())) << endl; // 1
    return 0;
}
```

例えば`----`は`--`二回分と認識され、`a.end()`の返すイテレータのメンバ変数`idx`を-2する。上記例ではどちらも`Array<double, 5> a`の先頭の値を参照しているため比較の際には等価を示す`1`が返る。
なお、`cout << (a.begin() == (----------a.end())) << endl;`のように書いたらエラーが発生する。このクラスには`==`演算子をオーバーロードしていないため当然ではある。

**普段さりげなく使ってきた`for(auto iter=begin(v); iter!=end(v); iter++);`の`++`の正体を知ることが出来てとても嬉しい。**

# おまけ コード全体

```cpp
#include <bits/stdc++.h>
#include <cstdlib>
using namespace std;

template <typename Array> 
struct Array_iterator{
    Array &a;
    size_t idx;
    Array_iterator(Array &a): a(a), idx(0){}
    typename Array::reference_type operator *(void){
        return a[idx];
    }
    Array_iterator operator ++(void){
        idx++;
        return *this;
    }
    Array_iterator operator --(void){
        idx--;
        return *this;
    }
};

template <typename T, size_t N>
struct Array{
    using value_type = T;
    using reference_type = T &;
    using const_reference_type = const T &;
    using size_type = size_t;
    using iterator_type = Array_iterator<Array>;
    value_type elements[N];
    reference_type operator [](size_type idx){
        cout << "A";
        return elements[idx];
    }
    const_reference_type operator [](size_type idx) const {
        cout << "B";
        return elements[idx];
    }
    size_type size(void);
    size_type size(void) const; // const修飾printで使う
    void print(void);
    void print(void) const;
    reference_type front(void);
    const_reference_type front(void) const;
    reference_type back(void);
    const_reference_type back(void) const;
    void fill(value_type);
    iterator_type begin(void);
    iterator_type end(void);
};

template <typename T, size_t N>
typename Array<T, N>::size_type Array<T, N>::size(void){
    return N;
}

template <typename T, size_t N>
typename Array<T, N>::size_type Array<T, N>::size(void) const {
    return N;
}

template <typename T, size_t N>
void Array<T, N>::print(void){
    for(Array<T, N>::size_type i=0; i<size(); i++){
        cout << elements[i] << " ";
    }
    cout << endl;
    return;
}

template <typename T, size_t N>
void Array<T, N>::print(void) const{
    for(Array<T, N>::size_type i=0; i<size(); i++){
        cout << elements[i] << " ";
    }
    cout << endl;
    return;
}

template <typename T, size_t N>
typename Array<T, N>::reference_type Array<T, N>::front(void){
    return elements[0];
}

template <typename T, size_t N>
typename Array<T, N>::const_reference_type Array<T, N>::front(void) const{
    return elements[0];
}

template <typename T, size_t N>
typename Array<T, N>::reference_type Array<T, N>::back(void){
    return elements[N-1];
}

template <typename T, size_t N>
typename Array<T, N>::const_reference_type Array<T, N>::back(void) const{
    return elements[N-1];
}

template <typename T, size_t N>
void Array<T, N>::fill(Array<T, N>::value_type value){
    for(size_t i=0; i<N; i++)elements[i]=value;
    return;
}

template <typename T, size_t N>
typename Array<T, N>::iterator_type Array<T, N>::begin(void){
    Array<T, N>::iterator_type res(*this);
    res.idx = 0;
    return res;
}

template <typename T, size_t N>
typename Array<T, N>::iterator_type Array<T, N>::end(void){
    Array<T, N>::iterator_type res(*this);
    res.idx = N;
    return res;
}

template <typename T, size_t N>
void print(Array<T, N> &array){ // Aの[]しか呼ばれない。あと参照型でarray全体のコピーをもらっていないため書き換えが可能
    for(size_t i=0, n=array.size(); i<n; i++){
        cout << array[i] << " ";
        // array[i] = array[i]+1; // 大本のデータが変更されうる
    }
    cout << endl;
    return;
}

template <typename T, size_t N>
void print(Array<T, N> const &array){ // Bの[]しか呼ばれない。参照型といっても書き換えしたら怒られる形に指定してるから書き換え不可能。
    for(size_t i=0, n=array.size(); i<n; i++){ // このsize()もconst修飾である必要がある
        cout << array[i] << " ";
        // array[i] = array[i]+1; // エラー
    }
    cout << endl;
    return;
}

int main(void){
    using Array_double5 = Array<double, 5>;
    Array_double5 a = {1.1, 2.2, 3.3, 4.4, 5.5}; 
    const Array_double5 b = {1.1, 2.2, 3.3, 4.4, 5.5}; 
    cout << *a.begin() << endl;
    cout << *(----------a.end()) << endl; // *(a.end())は配列外参照だから注意 [N-1]ではなく[N]を見ている。a.end()で返るイテレータに++しても更に外を見に行くからしないほうがいい。----は--二回分と認識されるため----------でここでのa.end()はa.begin()と等価になる。 
    cout << (*a.begin() == *(----------a.end())) << endl; // 1
    return 0;
}
```