Edited at

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

More than 1 year has passed since last update.


はじめに

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;
}



ソースコード

https://gist.github.com/tyanmahou/45341370b330f5cc5cabe850838031af