Search code examples
c++c++20c++-concepts

Understanding documentation for c++ concepts (weakly_incrementable)


I am trying to learn the concept of concepts in c++ and for this I wanted to create a type that is std::weakly_incrementable.

The concept is as follow :

template< class I >

    concept weakly_incrementable =
        std::movable<I> &&
        requires(I i) {
            typename std::iter_difference_t<I>;
            requires /*is-signed-integer-like*/<std::iter_difference_t<I>>;
            { ++i } -> std::same_as<I&>; // not required to be equality-preserving
            i++;                         // not required to be equality-preserving
        };

I understand from this that my type has to

  • have a preincrement that returns a reference to I
  • have a postincrement operator

so look something like :

struct I {
    void operator*() {}
    I& operator++() { return *this; }
    void operator++(int){}
};

However it is not clear what the type requirement asks for exacactly

typename std::iter_difference_t<I>;
requires /*is-signed-integer-like*/<std::iter_difference_t<I>>;

In the end, looking at examples I found that I need the following

struct I {
    using difference_type = int; // needed because of type requirement
    void operator*() {}
    I& operator++() { return *this; }
    void operator++(int){}
};

but how do I guess or understand from the documentation that I need to define a type called difference_type and not iter_difference_t or something else?


Solution

  • std::iter_difference_t is an alias template defined in the standard library.

    The requirement typename std::iter_difference_t<I>; is asking that std::iter_difference_t<I> is a valid type, and requires /*is-signed-integer-like*/<std::iter_difference_t<I>>; is asking that that type is signed-integer-like, which is described later on in the page.

    std::iter_difference_t<I> is std::iterator_traits<I>::difference_type if you specialize std::iterator_traits<I>, otherwise it is std::incrementable_traits<I>::difference_type.

    std::incrementable_traits<I> by default has a difference_type member that is either typename I::difference_type if that exists or std::make_signed_t<decltype(std::declval<I>() - std::declval<I>())> if there is an operator-.

    So there are 4 distinct ways to define std::iter_difference_t.

    1. Specialize std::iterator_traits<I>:

      struct I { I& operator++() { return *this; } void operator++(int) {} };
      template<>
      struct std::iterator_traits<I> {
          using difference_type = int;
      };
      static_assert(std::weakly_incrementable<I>);
      
    2. Specialize std::incrementable_triats<I>:

      struct I { I& operator++() { return *this; } void operator++(int) {} };
      template<>
      struct std::incrementable_traits<I> {
          using difference_type = int;
      };
      static_assert(std::weakly_incrementable<I>);
      
    3. Provide a member difference_type, like you did

    4. Provide an operator-:

      struct I { I& operator++() { return *this; } void operator++(int) {} };
      int operator-(const I&, const I&);
      static_assert(std::weakly_incrementable<I>);