span的正确性

350 阅读2分钟

span的正确性

span是具有引用语义的视图。从这个意义上讲,它们的行为类似于指针:当一个span为const时,并不自动意味着该span引用的元素是const。

这意味着您可以对const span的元素进行写访问(前提是这些元素不是const)。

std::array a1{1,2,3,4,5,6,7,8,9,10};

std::array a2{0, 8, 15};

const std::span<int> sp1{a1};

std::span<const int> sp2{a1};

sp1[0]=42;

sp2[0]=42;

sp1=a2;

sp2=a2;

注意,只要std::span<>的元素没有被声明为const,就会有一些操作提供对元素的写访问,即使对于const span(遵循普通容器的规则)也是如此。

·operator[],first(),last()

·data()

·begin(),end(),rbegin(),rend()

·std::cbegin(),std::cend(),std::crbegin(),std::crend()

· std::ranges::cbegin(), std: :ranges: :cend(), std::ranges::crbegin(),std::ranges::crend() 

是的,所有被设计为确保元素为const的c*函数都被std::span破坏。例如

template<typename T>

void modifyElems0fConstCollection (const T& coll)

{

	co11[0] = {}; 
	
	auto ptr = coll.data();

	*ptr={};



	for (auto pos= std::cbegin(col1); pos != std::cend(coll); ++pos) {

		*pos={};

	}

}

std::array a1{1,2,3,4,5,6,7,8,9,10};

modifyElems0fConstCollection(a1);
modifyElems0fConstCollection(std::span{a1}); 

这里的问题不是std::span被破坏了;问题是像std:: cbegin()std::ranges::cbegin()这样的函数目前对于具有引用语义的集合(例如视图)是被破坏的。

为确保函数只接受不能以这种方式修改元素的序列,可以要求const容器的begin()返回指向const元素的迭代器

template<typename T>

void ensureRead0nlyElemAccess (const T& coll)

requires std::is_const_v<std::remove_reference_t<decltype(*col1.begin())>> {

	...

}

至少,在标准化 C + + 20之后,甚至 std: : cstart ()也提供了写访问。

提供 cstart ()cend ()的全部目的是确保在迭代元素时不能修改元素。

最初,span 确实为 const _ iterator cstart ()cend ()类型提供了成员,以确保不能修改元素。然而,结果是 std: : cstart ()仍然迭代可变元素(std: : range: : cstart ()也有同样的问题)。

但是,没有修复 std: : cstart ()(和 std: : range: : cstart ()) ,而是删除了 span 中 const 迭代器的成员,这使问题变得更糟,因为现在已经没有简单的方法可以在 span 中进行只读迭代。

修复该问题的正确方法是修复 std: : cbegin ()的定义方式


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