LoginSignup
4
4

More than 5 years have passed since last update.

任意のメンバ関数でrange-based-forをする

Last updated at Posted at 2014-06-03

巷ではSwiftが話題ですが、C++の記事を投稿するのちょっと気がひけます...

さて、本題。
C++11から入ったrange-based-forは従来のイテレータを自分で回すのに
くらべforの中身、さらに参照外しをする必要がなくなったため
非常に便利になりました。
また、自作のクラスに対してもbegin,endを実装していれば
range-based-forを使うことができます。
しかし、begin,endしか対応していないのでたとえば
2通りのまわしたいものがあった時一方を犠牲にしなくてはなりません。
それはふべんだなーってことで、任意の名前のイテレータの開始と
終わりを示す関数を指定してrange-based-forを使えるようなアダプタを書いてみました。
といってもこれだとメンバ関数を指定する必要があるので、
完全にrange-based-forの恩恵を受けられるわけではありませんが...

ということでコード。
このサンプルではstd::vectorを降順、昇順でまわしています。

#include<iostream>
#include<vector>

template<typename T, typename U>
class Adopter{
    public:

        Adopter(T& obj, U (T::*obj_begin)(),U (T::*obj_end)()):
            obj(obj),
            obj_begin(obj_begin),
            obj_end(obj_end){}


        auto begin(){
            return (obj.*obj_begin)();  
        }

        auto end(){
            return (obj.*obj_end)();  
        }

    private:
        T& obj;
        U (T::*obj_begin)();
        U (T::*obj_end)();

};

template<typename T, typename U>
auto make_adopter(T& obj, U (T::*begin)(),U (T::*end)()){
    return Adopter<T,U>(obj,begin,end);
}

int main(){

    std::vector<int> Array = {1,2,3,4,5};

    for(auto elem : make_adopter(Array, &decltype(Array)::rbegin, &decltype(Array)::rend)){
        std::cout << elem << std::endl; 
    }

    for(auto elem : make_adopter(Array, &decltype(Array)::begin, &decltype(Array)::end)){
        std::cout << elem << std::endl; 
    }

    return 0;
}

していることとしてはbegin,endを提供する本体はAdopterクラス、
それに渡す型を推論し決定するためのmake_adopter関数で構成されています。
型はメンバ関数ポインタを使っているので非常に気持ち悪いことになっていますが、
それを受け取ってbegin,endメンバに移譲して使っているみたいな感じです。
また、型をいちいち書き下すのは面倒なのでC++14から入った戻り値の推論機能で
推論させています。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4