在阅读Eric Niebler的range proposal时, 我已经遇到过哨兵一词,作为最终迭代者的替代品 我很难理解哨兵对末端迭代器的好处 有人能提供一个明确的例子,说明发送给表的内容是不能用标准迭代器对完成的吗?
" sentinel 是对过去的迭代器的抽象。哨兵是 可用于表示范围结束的常规类型。一个 sentinel和表示范围的迭代器应为EqualityComparable。 当迭代器i比较等于时,sentinel表示一个元素 哨兵,我指向那个元素。" - N4382
我认为哨兵在确定范围的结束时起着作用的作用,而不仅仅是位置?
答案 0 :(得分:11)
Sentinel只允许结束迭代器使用不同的类型。
对于过去的迭代器允许的操作是有限的,但这并没有反映在它的类型中。 *
.end()
迭代器是不行的,但编译器会允许你。
哨兵没有一元解除引用,或++
等。它通常受限于一个超过结束迭代器的最弱迭代器,但在编译时强制执行。
有回报。经常检测结束状态比找到它更容易。使用哨兵,==
可以发送到"检测另一个参数是否超过了结束"在编译时,而不是运行时。
结果是,一些过去比C等效慢的代码现在编译为C级速度,例如使用std::copy
复制空终止字符串。如果没有哨兵,你要么必须扫描才能在复制之前找到结束,要么通过一个bool标记传递迭代器"我是最终的哨兵" (或等效的),并在==
上查看。
使用基于计数的范围时还有其他类似的优点。此外,一些像zip范围 1 这样的东西变得更容易表达(结尾zip哨兵可以容纳两个源哨兵,如果任何一个哨兵做的话,返回相等:zip迭代器要么只比较第一个迭代器,要么比较两者)。
另一种思考方式是算法倾向于不使用迭代器概念的完整丰富性作为过去的迭代器传递的参数,并且迭代器在实践中以不同的方式处理。 Sentinel意味着调用者可以利用这一事实,这反过来又让编译器更容易利用它。
1 拉链范围是您从2个或更多范围开始时得到的," zip"他们像拉链一样。范围现在超过了各个范围元素的元组。推进一个zip迭代器推进每个"包含"迭代器,同样用于解除引用和比较。
答案 1 :(得分:2)
Sentinels和end迭代器类似,它们标记范围的结尾。它们在如何检测到这一点方面存在差异;要么你正在测试迭代器本身,要么你在迭代器上测试数据值。如果您已经对数据进行了测试,那么哨兵可以让您的算法完成"免费"没有任何额外的测试。这可以简化代码,也可以加快代码。
一个非常常见的标记是用于标记字符串结尾的零字节。没有必要为字符串的末尾保留单独的迭代器,可以在使用字符串本身的字符时确定它。该约定的缺点是字符串不能包含零字符。
请注意,我在阅读链接中的提案之前写了这个答案;这是哨兵的经典定义,可能与那里提出的定义不一致。
答案 2 :(得分:0)
引入标记的主要动机是有很多迭代器操作得到支持但通常从不需要最终迭代器end()
。例如,通过*end()
取消引用它,通过++end()
递增它,依此类推(*)几乎没有任何意义。
相反,end()
的主要用法仅仅是将它与迭代器it
进行比较,以便表明it
是否位于它迭代的事物的末尾。并且,像编程中一样,不同的要求和不同的应用程序提出了一种新的类型。
range-v3库将这个观察转化为一个假设(通过一个概念实现):它为end()
引入了一个新类型,并且只要求它与相应的迭代器相当 - 但是不需要通常的迭代器操作)。这种新类型的end()
称为 sentinel 。
这里的主要优点是获得的抽象和更好的关注点分离,基于此,编译器可能能够执行更好的优化。在代码中,基本思想是这个(这只是为了解释而与range-v3库无关):
struct my_iterator; //some iterator
struct my_sentinel
{
bool is_at_end(my_iterator it) const
{
//here implement the logic when the iterator is at the end
}
};
auto operator==(my_iterator it, my_sentinel s) //also for (my_sentinel s, my_iterator it)
{
return s.is_at_end(it);
}
看抽象?现在,您可以在is_at_end
函数中实现任何所需的检查,例如:
N
增量后停止(以计算范围)\0
时停止,即*it = '\0'
(用于循环C字符串)此外,关于性能,可以在检查中使用编译时间信息(例如,将上面的N
视为编译时参数)。在这种情况下,编译器可能能够更好地优化代码。
(*)请注意,这并不意味着通常不会使用此类操作。例如,--end()
在某些地方可能很有用,例如, this question。但是,似乎可以在没有这些的情况下实现标准库 - 这就是range-v3库所做的。