constexpr な std::next を考えてみた

#include <utility>
#include <type_traits>
#include <iterator>

namespace pezzi
{
  // constexpr な forward
  template< class T >
  constexpr T&& forward( typename std::remove_reference<T>::type& x ) noexcept {
    return static_cast<T&&>(x);
  }
  // rvalue は forward 不可にする
  // int x; std::forward<unsigend>(x); みたいな例を ill-formed にするため
  template< class T >
  void forward( typename std::remove_reference<T>::type&& x ) = delete;
  
  // random access iter かつ literal type なら iter + 1 を返す
  template<
    class Iter_,
    class Iter = typename std::decay<Iter_>::type,
    class = typename std::enable_if<std::is_literal_type<Iter>::value>::type
  >
  constexpr Iter next_( Iter_ && iter, std::random_access_iterator_tag* ) {
    return pezzi::forward<Iter_>(iter) + 1;
  }
  
  // それ以外なら ++iter を返す
  template< class Iter  >
  Iter next_( Iter iter, void* ) {
    ++iter; return iter;  // move は要らない
  }
  
  // 本体
  template< class Iter_,
    class Iter = typename std::decay<Iter_>::type,
    class Category = typename std::iterator_traits<Iter>::iterator_category
  >
  constexpr Iter next( Iter_ && iter ) {
    return pezzi::next_( pezzi::forward<Iter_>(iter), static_cast<Category*>(0) );
  }
  
} // namespace pezzi


#include <deque>
#include <list>
#include <iostream>

int main()
{
  constexpr int a[3] = { 1, 2, 3 };
  /*constexpr*/ auto it0 = pezzi::next( a+0 );
  std::cout << *it0 << std::endl;
  
  std::deque<int> ls1 = { 1, 2, 3 };
  auto it1 = pezzi::next( ls1.begin() );
  std::cout << *it1 << std::endl;
  
  std::list<int> ls2 = { 1, 2, 3 };
  auto it2 = pezzi::next( ls2.begin() );
  std::cout << *it2 << std::endl;
  
}

中身は, random access iterator かつ literal type*1 ならば it+1 を,
そうでなければ ++it を返す,というだけのものです.
random access じゃないイテレータに対しても constexpr な next が欲しくなった場合には,適当に多重定義して下さい.


実装は,単純に tag dispatch と SFINAE を組み合わせただけなので,特に問題ないでしょう.


literal type じゃない random access iterator に対して, it+1 ではなく ++it を呼んでいるのは,
インライン化されない場合には,ステップ幅を渡さない operator++ の方が,より効率的だと判断したためです.
この辺は,いろいろと議論が必要だと思いますが,まぁ方針としては間違ってないはずです.

*1:要するに constexpr に出来るクラス