具有Sentinels 和计数的ranges定义

495 阅读2分钟

具有Sentinels 和计数的ranges定义

ranges可能不仅是容器或一对迭代器

ranges可以通过以下方式定义:

  • 相同类型的开始迭代器和结束迭代器
  • 开始迭代器和Sentinels (可能不同类型的结束标记)
  • 开始迭代器和计数
  • 数组

range支持所有这些ranges的定义和使用。

首先,算法的实现方式是ranges可以是数组。例如:

 int rawArray[] ={8,6,42,1,77};

...

std::ranges::sort(rawArray); 

//对原始数组中的元素进行排序

此外,还有几个实用程序可以定义由迭代器和Sentinels 或计数定义的ranges

subrange 子ranges

为了定义迭代器和Sentinels rangesranges库提供了类型 std::ranges::subrange<>。让我们看一个使用子ranges的简单示例:

#include <iostream> 
#include <compare>
#include <algorithm> //for for_each() 

struct NullTerm{

	bool operator== (auto pos) const {

		return *pos == '\0';  //end 是迭代器指向 '0 的位置' 
	}
};

int main()

 {

	const char* rawString = "hello world"; // 定义原始字符串和 NULL 终止符的`ranges`:

	std::ranges::subrange rawStringRange{rawString,Nu1lTerm{}}; //在算法中使用ranges

std::ranges::for_each(rawStringRange,

		[](char c){

		std::cout <<'·<<c; });

std::cout <<'\n';

// `ranges`总线 for 循环还支持迭代器/`Sentinels `: 

for (char c : rawStringRange){

	std::cout <<<<c; 

}

std::cout <<'\n'; 

}

作为Sentinels 示例介绍的那样,我们将类型Nu11Term定义为Sentinels 的类型检查字符串的空终止符作为ranges的末尾。

通过使用 std::ranges::subrange,程序定义了一个表示字符串开头和Sentinels 作为其结尾的ranges对象:

std::ranges::subrange rawStringRange{rawString,NullTerm{}};

ranges是泛型类型,可用于将迭代器和Sentinels 转换为将其表示为ranges的单个对象。事实上,ranges甚至是一个视图,它在内部只存储迭代器和Sentinels 。这意味着子ranges具有引用语义,并且复制它们的成本很低。

现在,我们可以将ranges传递给以ranges作为参数的新算法:

std::ranges::for_each(rawStringRange,

...);

请注意,子rangesbegin() end() 可能会产生不同的类型,因为它们只产生为定义ranges而传递的内容。

最后,请注意,基于 ranigefor 循环接受开始迭代器和结束迭代器类型不同的传递ranges(此功能已在 C++17 中引入,但对于ranges,您可以真正从中受益):

for (char c:std::ranges::subrange{rawString,NullTerm{}}){ 

    std::cout <<''<<c;

}

我们可以通过定义一个类模板来使这种方法更加通用,您可以在其中指定结束ranges的值。请考虑以下示例:

#include <iostream> 
#include <algorithm>

template<auto End> 

struct EndValue {

	bool operator==(auto pos) const { 

 	return *pos ==End; //end 是迭代器指向 End 的位置

	}
 };

int main() 

{

std::vector col1 = {42, 8, 0, 15, 7,-1}; 

// 定义一个`ranges` COLL 的开头和元素 7 作为结束:

std::ranges::subrange range{coll.begin(),EndValue<7>{}}; 

// 对此`ranges`的元素进行排序:


std::ranges::sort(range);

// 打印`ranges`的元素:

std::ranges::for_each(range,

		[](auto val){

		std::cout <<'<<val; 

		});

std::cout <<'\n';

// 打印 coll 的 ull 元素,最大为 -1:


std::ranges::for_each(coll.begin(),EndValue<-1>{},

		[](auto val) {

		std::cout<<'<<val;
		
		 });

std::cout <<'\n';

 }

在这里,我们将 EndValue<> 定义为结束迭代器,检查作为模板参数传递的结束。EndValue<7>{} 创建一个结束迭代器,其中 7 结束rangesEndValue<-1>{} 创建一个结束它 - erator,其中 1 结束ranges

程序的输出如下所示

0 8 15 42

0 8 15 42 7

可以定义任何受支持的非类型模板参数类型的值。

作为Sentinels 的另一个例子,看看std::unreachable_sentinel。这是 C++20 定义的值,用于表示无限ranges的“结束”。它可以帮助优化代码,使其永远不会与结尾进行比较(因为当它总是产生 false 时,这种比较是无用的)。


开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 N 天,点击查看活动详情