C++20范围的意义是什么?[已关闭]

q9rjltbz  于 2023-01-22  发布在  其他
关注(0)|答案(2)|浏览(148)

已关闭。此问题为opinion-based。当前不接受答案。
**想要改进此问题吗?**请更新此问题,以便editing this post可以用事实和引文来回答。

1年前关闭。
Improve this question
我很难理解c++20的范围和老式迭代器相比增加了什么。是的,我想没有必要再使用beginend了,但简单的重载,如:

namespace std {
    template <typename Container>
    auto transform(Container&& container, auto&&... args) requires ( requires {container.begin(); container.end(); }) {
         return transform(container.begin(), container.end(), args...);
    }
}

就能解决这个问题。
为什么范围是有用的,与迭代器相比,我应该在什么时候使用它们?
编辑:我知道范围比迭代器有其他的优势(链接,更好的方法等),但是,这些(我认为?)都可以用迭代器来完成,我不明白为什么需要引入一个全新的概念,比如范围。

6kkfgxo0

6kkfgxo01#

你反驳了自己的结论,证据如下:

template <typename Container>
auto transform(Container&& container, auto&&... args)
  requires ( requires {container.begin(); container.end(); }) {

那么......这是什么呢?这是一个函数,它接受一个满足约束的模板参数。让我们忽略这个约束需要成员begin/end,而不是更合理的std::ranges::begin/end要求。
你打算对多少个函数应用这个要求,可能很多,每个算法都会有一个版本,有这个要求,所以这看起来不像是一个一次性的要求,更像是一个命名为concept的东西。
特别是因为这个概念应该指定算法需要什么样的迭代器,你不仅仅需要成员begin/end;你需要它们返回一个input_or_output_iterator和一个sentinel_for迭代器:

requires ( requires(Container c)
{
  {c.begin()} -> input_or_output_iterator;
  {c.end()} -> sentinel_for<decltype(c.begin())>;
})

你真的想在每次请求“容器”时都输入这个吗?当然不想;这就是命名概念的作用。
那么这个概念是什么呢?它是一个你可以迭代的东西,一个值的序列,可以通过一个特定的迭代器接口访问。
这个概念的名字应该被选择为不暗示元素序列的所有权,transform算法不关心它所给定的东西是否拥有序列,所以“container”绝对是一个错误的名字。
我们把这个概念称为rangevalue-sequence。值序列可以通过迭代器/sentinel接口进行迭代。你可能需要有不同类别的值序列。输入序列、前向序列、连续序列等等。你可能想要检测序列是否可以在恒定时间内计算大小,或者序列是有界的还是从它的所有者借用的。
如果你能编写操作符来创建这些值序列的视图,那不是很好吗?
任何其他名字的范围闻起来都一样甜美。一旦你开始走上迭代器和哨兵配对的黑暗道路,它将永远主宰你的命运。
在处理迭代器时,范围是一个自然的概念,所有建立在范围概念之上的东西都是它的产物。

sshcrbum

sshcrbum2#

大多数标准库核心概念的要点,如迭代器,是统一标准库中常见的抽象。对于迭代器,这意味着提供一个接口,用于“这指向容器中的一个元素,我们希望能够迭代容器”的常用概念。
范围的意义在于隐藏公共用户界面中的原始迭代器。通常在迭代时,我们需要2个指针;容器的开始和结束。Ranges试图通过隐藏这个接口来简化这个过程,并为在开始()和end()之间操作的函数提供一个接口。
特别是,范围视图是使用范围的主要原因。当你想做函数组合时,它们允许更容易地阅读代码。cppreference的例子是一个很好用途:

#include <ranges>
#include <iostream>
 
int main()
{
    auto const ints = {0,1,2,3,4,5};
    auto even = [](int i) { return 0 == i % 2; };
    auto square = [](int i) { return i * i; };
 
    // "pipe" syntax of composing the views:
    for (int i : ints | std::views::filter(even) | std::views::transform(square)) {
        std::cout << i << ' ';
    }
 
    std::cout << '\n';
 
    // a traditional "functional" composing syntax:
    for (int i : std::views::transform(std::views::filter(ints, even), square)) {
        std::cout << i << ' ';
    }
}

与原始迭代器相比,范围和视图提供了一个更高层次的抽象层,成本几乎为零。在我个人看来,尤其是“视图管道”,与使用c++编写的没有范围的代码相比,更容易阅读和维护。

相关问题