0
#include <cstdint>
#include <ranges>

int main()
{
    auto const il = {1, 2, 3, 4, 5, 6};
    auto const n1 = std::int32_t{3};
    auto const n2 = std::uint32_t{3};
    auto const n3 = std::int64_t{3};
    auto const n4 = std::uint64_t{3};

    il | std::views::take(n1); // ok
    il | std::views::take(n2); // ok
    il | std::views::take(n3); // ok
    il | std::views::take(n4); // error
}

See online demo

Why can't compile std::views::take(std::uint64_t{})?

xmllmx
  • 39,765
  • 26
  • 162
  • 323

2 Answers2

3

Prior to P2367, the default case of views::take(E, F) was specified to be expression-equivalent to ranges::take_view{E, F}. Note the braces.

ranges::take_view<V> takes a range_difference_t<V> (which is signed) as its second argument. List-initialization rejects narrowing (e.g. attempting to initialize a ptrdiff_t with a uint64_t), so your example was rejected.

However, now views::take(E, F) is specified to be expression-equivalent to ranges::take_view(E, F). With parentheses. Which means no check for narrowing, which means passing a uint64_t is totally fine.

libstdc++ implemented that change on Apr 30 which I guess was too late for gcc 11.1 but that change is there in gcc 11.2, which accepts your code.

Barry
  • 286,269
  • 29
  • 621
  • 977
1

range | std::views::take(n) is expression equivalent to std::ranges::take_view(range, n), which takes a ranges::range_difference_t<V>.

Following the aliases through, gcc 11.1 doesn't want to consider unsigned long as usable where a long is wanted

Caleth
  • 52,200
  • 2
  • 44
  • 75