忍者ブログ

VIEWブログ

tk-xleaderのブログ。C++などプログラミングの話題が中心です。

【C++2a】std::ranges::begin, std::ranges::endの実装例

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

【C++2a】std::ranges::begin, std::ranges::endの実装例

C++2aで導入されるRangesライブラリでは、std::ranges::beginによってIteratorを、std::ranges::endによってSentinel*1を取り出すことができる型をRangeとして扱うことになっています。
そのstd::ranges::beginとstd::ranges::endですが、次のようなCustomization Point Objectになっています。どちらも、有効な呼び出しができないときはSFINAE-friendly error(実体化の失敗)になります。

式rに対して、std::ranges::begin(r) は、次の順に有効性をテストして、有効なものをdecay_copyしたものを意味します。
  1. rが配列のlvalueのときは、r + 0; // つまり、配列の最初の要素へのポインタ
  2. rがlvalueで、かつr.begin()がstd::ranges::iteratorコンセプトを満たす型を返す式として有効であるときは、 r.begin();
  3. オーバーロード集合として、template<typename T> void begin(T&&) = delete; template<typename T> void begin(std::initializer_list<T>&&) = delete; を加えたうえで、begin(static_cast<decltype(r)&&>(r))がstd::ranges::iteratorコンセプトを満たす型を返す式として有効であるときは、begin(static_cast<decltype(r)&&>(r));
そして、式rに対して、std::ranges::end(r)は、次の順に有効性をテストして、有効なものをdecay_copyしたものを意味します。
  1. rが配列のlvalueのときは、r + std::extend_v<decltype(r)>;
  2. rがlvalueで、r.endがstd::ranges::begin(r)の戻り値型に対するstd::ranges::sentinelコンセプトを満たす型を返す式として有効であるときは、r.end();
  3. オーバーロード集合として、template<typename T> void end(T&&) = delete; template<typename T> void end(std::initializer_list<T>&&) = delete; を加えたうえで、end(static_cast<decltype(r)&&>(r))がstd::ranges::begin(r)の戻り値型に対するstd::ranges::sentinelコンセプトを満たす型を返す式として有効であるときは、end(static_cast<decltype(r)&&>(r));
rvalueに対してはフリー関数のbegin/endに限定しているのは、rvalueは寿命がすぐに尽きる値を意味する*2ので、そこから取り出したIterator/Sentinelが無効領域を指すものとなってしまう危険があるからです。
rvalueに対して有効にIterator/Sentinelを呼び出したい場合、ADLを通してbegin/endが呼び出せるようにします。標準では、実体の寿命を管理しないstd::string_viewやstd::spanは、friend関数としてbegin/endを定義しています。
これらを踏まえて、std::ranges::begin/endを実装すると、こうなります。
namespace std::range{
  namespace __begin_impl_ns{
    template<typename T> void begin(T&&) = delete;
    template<typename T> void begin(std::initializer_list<T>&&) = delete;
    class _begin_functor{
      template<typename E, std::size_t size>
      static E* impl(E (&arr)[size]){
        return arr;
      }
template<typename E> static E* impl(E (&arr)[]){ return arr; } template<typename R> requires iterator_type<decltype((std::declval<R&>()).begin())> static auto impl(R& r){ return r.begin(); } template<typename R> requires iterator_type<decltype(begin(std::declval<R>()))> static auto impl(R&& r){ return begin(std::forward<R>(r)); } public: template<typename R> auto operator()(R&& r)->decltype(impl(std::forward<R>(r)))const{ return impl(std::forward<R>(r)); } }; } inline constexpr __begin_impl_ns::_begin_functor begin{}; namespace __end_impl_ns{ template<typename T> void end(T&&) = delete; template<typename T> void end(std::initializer_list<T>&&) = delete; class _end_functor{ template<typename E, std::size_t size> static E* impl(E (&arr)[size]){ return arr + size; }
template<typename E> static E* impl(E(&arr)[]){ return arr; } template<typename R> requires sentinel_for<decltype((std::declval<R&>()).end()),decltype(ranges::begin(std::declval<R&>()))> static auto impl(R& r){ return r.end(); } template<typename R> requires sentinel_for<decltype(end(std::declval<R>())),decltype(ranges::begin(std::declval<R>()))> static auto impl(R&& r){ return end(std::forward<R>(r)); } public: template<typename R> auto operator()(R&& r)->decltype(impl(std::forward<R>(r)))const{ return impl(std::forward<R>(r)); } }; } inline constexpr __end_impl_ns::_end_functor end{}; }
  1. 直訳すると「番兵」、Rangesライブラリでは、Iteratorと比較演算子で比較でき、要素の最後の一つ後ろのIteratorと等価のものとして扱われる。Iteratorと同じ型であってもよい。
  2. アルゴリズム関数は、RangeをR&&で受け取ってそのまま(perfect forwardingせずに)std::ranges::begin/endに渡すので、アルゴリズム関数にはrvalueを渡すことができます。
PR

コメント

プロフィール

HN:
tk-xleader
Webサイト:
性別:
男性

カテゴリー

P R