LoginSignup
10
9

More than 5 years have passed since last update.

構造化束縛:範囲ベースforでindexも取得する

Last updated at Posted at 2017-10-22

はじめに

C++17から使える構造化束縛を使えばindex付きの範囲ベースforをいい感じに書けるのでは?って思ったのでまとめとく

10/31追記 constにしたい時はas_constで良かった

範囲ベースでindex取得

もとのイテレーターの値への参照とindexを返すWithIndexIteratorを作成し、
それを返すclassを作る

Range.hpp
#pragma once

#include<iterator>

namespace range
{
    template<class Range>
    using range_iterator_t = decltype(std::begin(std::declval<Range&>()));

    template<class Range>
    using range_const_iterator_t = decltype(std::cbegin(std::declval<Range&>()));

    namespace detail
    {
        //pair(普通のstd::pairでも良い)
        template<class T>
        struct WithIndexPair
        {
            T value;
            const std::size_t index;
        };
        //イテレーター
        template<class It>
        class WithIndexIterator
        {
        public:
            using iterator_category = std::bidirectional_iterator_tag;
            using difference_type = std::size_t;
            using value_type = WithIndexPair<decltype(*std::declval<It>())&>;
            using pointer = value_type*;
            using reference = value_type&;
        private:
            It m_iterator;
            std::size_t m_index;
        public:
            WithIndexIterator(It it, std::size_t index) :
                m_iterator(it),
                m_index(index)
            {}

            value_type operator *()const
            {
                return { *m_iterator, m_index };
            }
            value_type* operator ->()const
            {
                return &*(*this);
            }
            WithIndexIterator& operator ++()
            {
                ++m_iterator;
                ++m_index;
                return *this;
            }
            WithIndexIterator& operator --()
            {
                --m_iterator;
                --m_index;
                return *this;
            }
            bool operator ==(const WithIndexIterator& other)const
            {
                return m_iterator == other.m_iterator;
            }
            bool operator !=(const WithIndexIterator& other)const
            {
                return m_iterator != other.m_iterator;
            }

        };
        //レンジ
        template<class Range>
        class WithIndexRange
        {
            using iterator = WithIndexIterator<range_iterator_t<Range>>;
            using const_iterator = WithIndexIterator<range_const_iterator_t<Range>>;

        private:
            Range m_range;
        public:

            WithIndexRange(Range&& range) :
                m_range(std::forward<Range>(range))
            {}

            iterator begin()
            {
                return { std::begin(m_range),0 };
            }
            iterator end()
            {
                return { std::end(m_range) , std::size(m_range) };
            }

            const_iterator begin()const
            {
                return const_iterator{ std::begin(m_range),0 };
            }
            const_iterator end()const
            {
                return const_iterator{ std::end(m_range) , std::size(m_range) };
            }

            std::size_t size()const
            {
                return std::size(m_range);
            }

        };
    }

    //添え字付き
    constexpr struct _WithIndex_OP
    {
        template<class Range>
        auto operator()(Range&& v)const
        {
            return detail::WithIndexRange<Range>(std::forward<Range>(v));
        }
        template<class Range>
        friend auto operator -(Range&& v, _WithIndex_OP op)
        {
            return op(std::forward<Range>(v));
        }
    }withIndex;


}



以下みたいに値とindexをとることができる。

また、メンバの参照型はconstで受け取っても変更可能なので注意
constにしたい場合はstd::as_constを使うことで使い分けができる(右辺値は無理だが…)

main.cpp
int main()
{
    std::vector<int> v{ 1,2,3 };

    //添え字つき
    for (auto&&elm: v - range::with_index)
    {
        std::cout << "Value :" << elm.value <<
                    " Index :" << elm.index << std::endl;
    }
    //添え字つき const版
    for (auto&&elm : std::as_const(v) - range::with_index)
    {
        //エラー
        //elm.value += 1;
    }
    return 0;
}

ここまではC++17じゃなくてもいい

構造化束縛

C++17から使える構造化束縛が便利

main.cpp
decltype(auto) test1()
{
    return std::make_tuple("Hellow", 1);
}
decltype(auto) test2()
{
    struct
    {
        int m_x;
        int m_y;
    }ret{33,4};
    return ret;
}
decltype(auto) test3()
{
    return std::make_pair(1,0.5);
}


int main()
{

    {
        auto [str, i] = test1();        //tuple
    }
    {
        auto [x, y] = test2();          //struct
    }
    {
        int raw_ar[3] = { 1,2,3 };
        auto[x, y, z] = raw_ar;         //raw array
    }
    {
        std::array<int, 3> ar{ 4,5,6 }; //stl array
        auto [x, y, z] = ar;
    }
    {
        auto [i, d] = test3();          //pair
    }   

    return 0;
}



auto&,const auto&,auto&&などでも受け取れる
std::tieを楽にした感じ

で、この機能と先ほどのindex取得を組み合わせれば
下みたいにかける

main.cpp


int main()
{
    std::vector<int> v{ 1,2,3 };

    //添え字つき
    for (auto&&[elm,i]: v - range::with_index)
    {
        std::cout << "Value :" << elm <<
                    " Index :" << i << std::endl;
    }
    return 0;
}

わりと好み

おまけ

関係ないけど逆順範囲forなども作ってみた

main.cpp

int main()
{

    std::vector<int> v{ 1,2,3 };

    for (auto&& elm: v - range::reverse)
    {
        std::cout << elm;
    }

    return 0;
}

ソースコード

10
9
1

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
10
9