为什么 std::ranges::views::take 使用模板化类型来区分?

Why is std::ranges::views::take using templated type for difference?

签名是

template< ranges::viewable_range R, class DifferenceType >

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

这是一件小事,但我想知道为什么 DifferenceType 不是某种 ssize 类型(在现代机器上实际上是 int64_t)。 这只是为了避免在比较不同符号的整数时发出警告,还是我遗漏了其他一些设计原因。我的直觉是固定类型会使错误消息更易于阅读,所以我想知道动机是什么。

如果有人对代码示例感兴趣,就在这里,感觉有点做作,因为我想保持简短,但很有可能传递错误的类型来接受参数,然后错误消息是 unreadable(我最喜欢的部分是:

/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);
}

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.

不同范围适配器的迭代器具有不同的 difference_typedifference_type的计算有时候并没有你想的那么简单

iota_view为例,标准故意用IOTA-DIFF-T(W)来计算其difference_type,使得iota_view<uint64_t>difference_type__int128iota_view<__int128>difference_type 甚至是 listdc++ 中的自定义 __detail::__max_diff_type

这就是为什么views::take的第二个参数是模板而不是具体的整数类型的原因,但是注意标准对[range.take.overview]中的DifferenceType也有约束:

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.

这要求 DifferenceType 必须可以转换为 Rdifference_type,这也允许您

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