为什么标准将borrowed_subrange_t 定义为common_range?

如何解决为什么标准将borrowed_subrange_t 定义为common_range?

C++20 引入了 ranges::borrowed_range,它定义了一个范围的要求,以便函数可以按值获取它并返回从中获得的迭代器,而不会出现悬空的危险。简而言之(其中 参考P2017R1):

当范围超出范围后您可以保留其迭代器时,范围就是借用范围。

同时,还引入了一个类型助手borrowed_subrange_t

template<ranges::range R>
using borrowed_subrange_t = std::conditional_t<
    ranges::borrowed_range<R>,ranges::subrange<ranges::iterator_t<R>>,ranges::dangling
>;

这是一个别名模板,一些受约束的算法(例如 ranges::uniqueranges::find_end)使用它来避免返回潜在的悬空迭代器或视图。

当类型 R 模型 borrowed_range 时,borrowed_subrange_tR 基本上是一个 subrange<ranges::iterator_t<R>>, 这意味着它也是一个 ranges::common_range,因为它只需要一个模板参数,第二个默认与第一个类型相同。

但似乎有些误导,因为有一些 subrange 类型可以借用但仍然不是 common_range,请考虑以下代码:

auto r = views::iota(0);
auto s1 = ranges::subrange{r.begin(),r.begin() + 5};
auto s2 = ranges::subrange{r.begin() + 5,r.end()};

我从 subrange ranges::iota_view 创建了两个 borrowed_range,一个包含前 5 个元素,另一个包含 itoa_view 从第五个元素开始的所有元素.它们是 subrangeitoa_view,显然是借来的:

static_assert(ranges::borrowed_range<decltype(s1)>);
static_assert(ranges::borrowed_range<decltype(s2)>);

所以在某种程度上,它们的类型都可以看作是borrowed_subrange_t类型的itoa_view,但是根据定义,只有s1的类型是{{1 borrowed_subrange_t 类型的 }},这也意味着以下代码格式错误,因为 r iota_view 不是 r

common_range

为什么标准需要保证一些auto bsr = ranges::borrowed_subrange_t<decltype(r)>{r}; // ill-formed 的{​​{1}}是borrowed_subrange_t,即range和{的返回类型{1}} 一样吗?这背后的原因是什么?为什么不更一般地定义它:

R

这样做会不会有任何潜在的缺陷和危险?

解决方法

引用 Alexander Stepanov 在“从数学到泛型编程”中的话:

在编写代码时,通常情况下您最终会计算出调用函数当前不需要的值。但是,稍后在不同情况下调用代码时,此值可能很重要。在这种情况下,你应该遵守有用返回定律:一个过程应该返回它计算出的所有可能有用的信息。

borrowed_subrange 用于必然遍历整个子范围的算法。所以我们必须计算这个范围的结束迭代器作为执行算法其余部分的副作用。这对用户很有用,所以我们应该返回它!

对于其中一些算法,实际上甚至不可能返回哨兵。例如,ranges::search 必须返回匹配的子范围 - 但该子范围不必位于初始范围的最末端,因此返回原始标记根本不是一种选择。

对于其他算法,返回哨兵可能是一种选择,但这是一个糟糕的选择。考虑unique。这里基本上有三个选择:

  1. 仅返回表示此范围开始的迭代器 (I)(如 std::unique 所做的那样)
  2. 返回 subrange<I,S> 表示完整范围(即仅通过提供的 last
  3. 返回 subrange<I> 表示完整范围,包括计算出的 I 引用 last

但我们已经在做能够做 (3) 的工作,所以这更有价值。没有理由做(2)。


考虑一个不那么抽象的情况,我们实际上有一个哨兵。假设我们有一个以空字符结尾的字符串:

struct null_terminated_string {
    char const* p;

    struct sentinel {
        auto operator==(char const* p) const { return *p == '\0'; }
    };

    auto begin() const -> char const* { return p; }
    auto end() const -> sentinel { return {}; }
};

现在,从 unique 返回更有用的返回值是什么:返回此 null_terminated_string::sentinel 类型的返回值或返回指向空终止符的 char const* 类型的返回值?后者为您提供了更多有用的信息(例如,包括尺寸!)。


最后,这个:

template <ranges::range R>
using borrowed_subrange_t = std::conditional_t<
    ranges::borrowed_range<R>,ranges::subrange<
      ranges::iterator_t<R>,std::common_iterator<
        ranges::iterator_t<R>,ranges::sentinel_t<R>
      >
    >,ranges::dangling
>;

没有意义,因为 common_iterator<iterator_t<R>,sentinel_t<R>> 不是 iterator_t<R> 的哨兵。应该是这样的:

template <ranges::range R>
using borrowed_subrange_t = std::conditional_t<
    ranges::borrowed_range<R>,ranges::subrange<ranges::iterator_t<R>,ranges::sentinel_t<R>>,ranges::dangling
>;

而且这可能是有道理的。考虑ranges::find。现在,它只是返回一个 iterator_t<R>(或者更准确地说,一个 iterator_t<R>dangling)。但是 ranges::find 的不同设计可以做一些不同的事情:它可以返回一个从该迭代器开始并包括整个其余范围的子范围(可以说这会更有用)。如果我们想为 ranges::find 这样做,我们肯定想要返回一个 subrange<iterator_t<R>,sentinel_t<R>>。在这种情况下,我们还没有遍历整个范围,我们不想为此支付额外的成本;我们会简单地通过哨兵转发。

只是在 <algorithm> 中没有任何看起来像这样的算法,这些算法只是将迭代器而不是子范围返回到末尾。如果我们有这样的算法,我们肯定会有一个使用 borrowed_subrangesentinel_t<R> 版本。但是根据我们现有的算法,不需要这样的东西。

,

为什么标准需要保证某个范围R的borrowed_subrange_t是common_range,即begin()和end()的返回类型相同?

并非所有子范围都以基础范围的标记值结束。

这样做会不会有任何潜在的缺陷和危险?

如果基础范围的类型为空类型作为标记,则所有子范围都将在标记处结束,而不是在它们想要的结束处。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res