【问题标题】:Why doesn't ranges provide a type erased view for non-contiguous ranges?为什么范围不为非连续范围提供类型擦除视图?
【发布时间】:2021-10-05 13:30:05
【问题描述】:

我想要一个函数,它可以采用任何固定值类型的范围/视图。

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

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

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

【问题讨论】:

  • 你的类型都是arrays吗?您的问题仅以这些为例,而您假设的 view&lt;T&gt; 仅适用于连续范围...因此,如果是这种情况,则 this is the same question 具有相同的答案:使用 span
  • 是的,这是我的问题。但我一直在寻找对范围和概念的理解,而不是解决引发问题的问题
  • 我的意思是,如果问题是“为什么范围不为连续范围提供类型擦除视图?”答案是“确实如此:span&lt;T&gt;”。如果问题是不是,那么澄清问题是什么会很有帮助。也许,删除连续的部分?
  • @TomHuntington:这个问题真的只是关于 IDE 支持吗?因为无论是语言还是库都不存在让您的 IDE 满意。如果你想让它快乐,那么只需使用实际类型而不是auto。毕竟是你写的。
  • @TomHuntington:一般来说,如果您别无选择,您只会对算法或范围的其他消费者使用类型擦除。实际上,所有形式的类型擦除也是如此。

标签: c++ iterator c++20 c++-concepts std-ranges


【解决方案1】:

range-v3 的名称为 any_view&lt;Ref, Cat&gt;,其中 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&lt;T&gt; 在这方面很棒。

或者没有人关心这样做,因为唯一的原因是 ide 支持?

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

[...] 但是 Visual Studio IDE 不知道 element 的类型。

你也可以不使用auto来解决这个问题。

【讨论】:

  • 当我已经有std::spanstd::array 一起使用时,我可以看到你很少想要使用它。 container&lt;T&gt; 返回 container&lt;T&gt;::iterator 类型的迭代器而不是 iterator&lt;T&gt; 是否有原因?
  • @TomHuntington vector&lt;int&gt;list&lt;int&gt;deque&lt;int&gt;forward_list&lt;int&gt; 的迭代器具有非常不同的特征 - 它们不可能都是相同的类型。
  • 接口不是零成本的抽象,所以我们需要概念和模板,这就是 c++ 的特别之处。感谢您的宝贵时间!
猜你喜欢
  • 1970-01-01
  • 2012-08-26
  • 2017-07-16
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 2023-01-12
  • 2020-04-02
相关资源
最近更新 更多