はじめに
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;
}
ソースコード