2014年1月13日月曜日

C++11のrange based forで添字を使えるようにしてみた

ということで、新年始まって2週間近く立ちましたが、今更ながら、今年最初の記事を書くことにします。

C++の範囲for便利ですよね。

   QVector<QVector<int>> array{{0,1},{2,3,4},{5,6,7,8}};
   //2次元の動的配列の全要素に安全にアクセス
   for(auto i:array){
       for(auto j:i){
           qDebug() << j;
       }
   }

もう、i++とj++を書き間違えて泣くことは有ない訳です、

でも、ちょ〜っと、気が聞きかない所があります。
イテレータの実装によりますが、基本的にアドレス演算しかしないので、
今何回めのループなのか知りたい場合、範囲forは使えません

   QVector<int> array{0,1,2,3,4,5,6,7,8};
   QVector<int> array2(array.count());
   for(auto elem:array){
       //何回目?
       array2[i]=elem;
   }

まぁ、単なる複製なら、=演算子を使えばいいだけなのですが、
配列に格納したクラスのメッソドの返り値を別の配列に格納するとかの場合、使い勝手が悪い。
末尾追加はreserveで予め領域を確保した場合でも、判定が入る分微妙に遅くなります。

普通のforで書くしか無い。
   QVector<int> array{0,1,2,3,4,5,6,7,8};
   QVector<int> array2(array.count());
   //普通のforで書く
   for(int i=0;i<array.count();i++){
       //何回目か分かる でも 面倒くさい
       array2[i]=array[i];
   }
ループの度に、一々判定式と再初期化式を書くのは、うっかりミスの温床なので、できれば避けたい。

添字を返すイテレータ 


   QVector<int> array{0,1,2,3,4,5,6,7,8};
   QVector<int> array2(array.count());
   for(const int i:SmartCountor(array.count())){
       //何回目か分かる
       array2[i]=array[i];
   }
SmartCounterはイテレータを返すコンテナ、コンストラクタで指定された回数ループします、
副産物として、普通のforと違い、添字に使う整数にconst修飾ができます。

第一引数に始点、第二引数に、ループ回数を設定できます。
   QVector<int> array{0,1,2,3,4,5,6,7,8};
   QVector<int> array2(array.count());
   //第一引数に 開始位置 第二引数に ループ回数 *終了位置ではないので注意 
   for(const int i:SmartCountor(4,array.count()-4)){
       //何回目か分かる
       array2[i]=array[i];
   }
この場合第一引数には負数も設定可能です。
そのまま配列へのアクセスとして使うと、領域違反が起きるので注意、
それ以外の引数に負数が与えられた場合、自動的に0になります。

以下ソースコード

#ifndef SMARTCOUNTER_H
#define SMARTCOUNTER_H


//範囲for用の  添字イテレータ intを返す
class CountIterator
{
public:
    constexpr CountIterator(int set):index(set){;}
   //デリファレンス
    constexpr int operator *() const
    {
        return index;
    }
    //非一致判定
    constexpr bool operator !=(CountIterator set) const{
        return (index!=set.Index())?(true):(false);
    }
    //インクリメント
    void operator ++()
    {
        index++;
    }
    //比較用関数
    constexpr int Index() const {
        return index;
    }
private:
    int index;
};

//範囲for用のカウンタ
class SmartCountor
{
public:
    constexpr SmartCountor(int size):
        index(0),countSize(((size<0)?(0):(size))){;}
    constexpr SmartCountor(const int pos,const int length):
        index(pos),countSize(((length)<0)?(pos):(pos+length)){}
    constexpr CountIterator begin() const {return CountIterator(index);}
    constexpr CountIterator end() const {return CountIterator(countSize);}
private:
    int index;
    int countSize;
};


#endif // SMARTCOUNTER_H