Home > front end >  Why is std::ranges::views::take using templated type for difference?
Why is std::ranges::views::take using templated type for difference?

Time:04-03

Signature of take is

template< ranges::viewable_range R, class DifferenceType >

    requires /* ... */
constexpr ranges::view auto take( R&& r, DifferenceType&& count );

It is a minor thing but I wonder why DifferenceType is not some ssize type(practically int64_t on modern machines). Is this just to avoid warnings on comparisons of integers of different signednes, or is there some other design reason I am missing. My intuition that having a fixed type would make error messages easier to read, so I wonder what was the motivation.

If somebody is interested in code example, here it is, it feels a bit artificial since I wanted to keep it short, but it is quite possible to pass wrong type to take argument and then error message is unreadable(my favorite part is:

/opt/compiler-explorer/gcc-trunk-20220401/include/c /12.0.1/bits/atomic_base.h:98:3: note: candidate: 'constexpr std::memory_order std::operator|(memory_order, __memory_order_modifier)'

).

#include <vector>
#include <iostream>
#include <ranges>
#include <string>
#include <fmt/format.h>
#include <fmt/ranges.h>

auto get_n(){
    return std::string("2");
}

int main() {
    std::vector v {2,3,5,7,11};
    const auto n = get_n(); //oops this is std::string
    auto dont_even = v | std::views::filter([](const int& i){return i%2==1;}) | std::views::take(n);
    std::cout << fmt::format("{}", dont_even);
}

CodePudding user response:

It is a minor thing but I wonder why DifferenceType is not some ssize type(practically int64_t on modern machines). Is this just to avoid warnings on comparisons of integers of different signednes, or is there some other design reason I am missing.

Iterators for different range adaptors have different difference_types. The calculation of difference_type is sometimes not as simple as you think.

Taking iota_view as an example, the standard deliberately uses IOTA-DIFF-T(W) to calculate its difference_type, which makes the difference_type of iota_view<uint64_t> is __int128 and the difference_type of iota_view<__int128> is even a customized __detail::__max_diff_type in listdc .

This is why the second parameter of views::take is a template and not a specific integer type, but note that the standard also has constraints on DifferenceType in [range.take.overview]:

The name views​::​take denotes a range adaptor object ([range.adaptor.object]). Let E and F be expressions, let T be remove_­cvref_­t<decltype((E))>, and let D be range_­difference_­t<decltype((E))>. If decltype((F)) does not model convertible_­to<D>, views​::​take(E, F) is ill-formed.

which requires that the DifferenceType must be convertible to R's difference_type, which also allows you to

auto r = std::views::iota(0)
       | std::views::take(std::integral_constant<int, 42>{});
  • Related