Home > other >  How to use iota_view for custom IntLike types?
How to use iota_view for custom IntLike types?

Time:12-06

I would like to use std::ranges::iota_view for a custom data type that is "int" like in the same way I would use std::ranges::iota_view for an int. I couldn't figure out how to adjust the code based on gcc's error messages, which are posted below:

#include <ranges>
#include <limits>

struct IntLike {
    IntLike(int val = {}) : m_val{val} {}
    operator int() const { return m_val; }
    IntLike& operator  () {   m_val; return *this; }
    IntLike operator  (int) { IntLike ret = *this;   m_val; return ret; }
    friend auto operator <=>(IntLike lhs, IntLike rhs) = default;

    int m_val;
};

namespace std {
    template<>
    struct iterator_traits<IntLike> {
        using difference_type = IntLike;
    };
}

int main()
{
    static_assert(std::same_as<std::iter_difference_t<int>, int>);
    static_assert(std::same_as<std::iter_difference_t<IntLike>, IntLike>);
    std::ranges::iota_view(0, 2);
    std::ranges::iota_view(IntLike{0}, IntLike{2});
    return 0;
}

Errors:

<source>:26:50: error: class template argument deduction failed:
   26 |     std::ranges::iota_view(IntLike{0}, IntLike{2});
      |                                                  ^
<source>:26:50: error: no matching function for call to 'iota_view(IntLike, IntLike)'
In file included from <source>:1:
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:617:7: note: candidate: 'template<class _Winc, class _Bound> iota_view(typename std::ranges::iota_view<_Winc, _Bound>::_Iterator, typename std::ranges::iota_view<_Winc, _Bound>::_Sentinel)-> std::ranges::iota_view<_Winc, _Bound> requires !(same_as<_Winc, _Bound>) && !(same_as<_Bound, std::unreachable_sentinel_t>)'
  617 |       iota_view(_Iterator __first, _Sentinel __last)
      |       ^~~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/ranges:617:7: note:   template argument deduction/substitution failed:
<source>:26:50: note:   couldn't deduce template parameter '_Winc'

CodePudding user response:

This is non-trivial and, even when it works, it is highly limited.

The first problem is the constructor. The constructor you are trying to use is the one that takes the starting point and the boundary condition. However, this constructor is deliberately prevented from using class template argument deduction through the use of std::type_identity_t. Instead, an explicit deduction guide is used.

However, this guide requires that the types in question are "integer-like". And it is not possible for user-code to create a type that is "integer-like"; only implementations can create classes that are "integer-like". So this deduction guide never applies.

That's why the compiler thinks you're trying to pass a pair of iterators.

Now, you can bypass the deduction guide by specifying the types directly (though you'll never be able to use views::iota, since it always uses the guides). But even if you do, your type must conform to a bunch of concepts: weakly_incrementable, __WeaklyEqualityComparableWith, advanceable&decrementable, and probably a few others. You'll also need to provide an iota-diff-t for your type.

Overall... it's probably not worth the effort. It would be a much better use of your time to take a regular iota range and apply a views::transform to convert each integer to the actual type you desire. Like views::iota(...) | views::transform([](int i) { return IntLike(i);}).

CodePudding user response:

The first template parameter W of iota_view requires it to model weakly_incrementable, which requires that its difference type must be signed-integer-like type, which includes signed integer type and signed integer-class type. The latter is an implementation-defined type with integer behavior, which cannot be defined by the user.

Since the difference type of IntLike is IntLike, which is not a signed-integer-like type, the constraint is not satisfied.

The simplest workaround is to define the difference_type of IntLike as ptrdiff_t which has enough width to accommodate the subtracted value of the maximum and minimum values of int.

namespace std {
    template<>
    struct iterator_traits<IntLike> {
        using difference_type = ptrdiff_t;
    };
}
  • Related