为什么范围不为非连续范围提供类型擦除视图?

Why doesn't ranges provide a type erased view for non-contiguous ranges?

我想要一个函数,它可以接受任何 range/view 固定值类型。

int main()
{
    std::array<std::pair<int, int>, 2> a{...};
    std::array<std::pair<int, int>, 3> b{...};
    generic_fun(a);
    generic_fun(b);

};

当然可以

template <std::ranges::range R>
    requires std::same_as<std::ranges::range_value_t<R>,std::pair<int,int>>
auto generic_fun(R range)
{
    for(const auto& element : range)
        return element.first;
}

但是 visual studio ide 不知道 element 的类型。

我期待范围库有一些类似

的类型
template <typename T>
struct view
{
    template <std::ranges::range R>
        requires std::same_as<std::ranges::range_value_t<R>, T>
    view(R);
    T* begin() const;
    T* end() const;
};

这会给我 ide 支持

auto generic_fun(view<std::pair<int,int>> a)
{
    for (const auto& b : a)
        return b.first;
}

为什么范围库中不存在这样的类型?定义一个抽象出除 ranges/iterators 值类型以外的所有类型的类型在技术上是不可行的吗?或者没有人关心这样做,因为唯一的原因是 ide 支持?

虽然拥有一个包装给定迭代器概念的类型(在给定值类型上模板化)是很自然的,但 std 没有定义任何。 (如果互联网上有关于包装概念的类型的内容,请指点一下好吗?)

range-v3 在名称 any_view<Ref, Cat> 下有这个,其中 Ref 是范围的 reference(不是 value_type),Cat 是迭代类别(默认为 input)。那会让你写一个像这样的函数:

int sum(any_view<int const&> v) { // <== not a function template
    int s = 0;
    for (int i : v) {
        s += i;
    }
    return s;
}

std::list<int> l = {1, 2, 3};
assert(sum(l) == 3);

问题是,这个射程适配器 非常 昂贵。想想迭代的工作方式。我们有一个像这样的循环:

for (; it != end; ++it) {
    use(*it);
}

即使对于输入迭代器,这也意味着您必须键入擦除:

  • operator!=
  • operator++
  • operator*

那是 三个 间接调用每个元素virtual 函数调用,或通过函数指针,具体取决于执行)。这是一个很大的开销,而且它很少是您真正想要使用的东西。因此,就添加到 C++20 中的范围适配器而言,它的优先级非常低,甚至不在我们 plan 中用于将来添加范围适配器。

现在,对于连续的范围,情况有点不同,因为您始终可以只存储 (T*, size_t),而原始范围是什么并不重要。它仍然是类型擦除,但它是免费的——没有额外的开销。所以 span<T> 在这方面很棒。

Or does no-one care about doing this because the only reason is ide support?

有些情况下类型擦除实际上很重要。也许您正在存储一堆不同类型的范围。也许您在 ABI 边界上隐藏了它。但是 IDE 支持似乎是使用类型擦除的一个非常微弱的动机——尤其是在这样的上下文中,这样做会产生相当大的性能开销。

[...] but then the visual studio ide doesn't know the type of element.

您也可以通过不使用 auto.

来解决这个问题