指针差异是否是在基于范围的 for 循环中查找向量中元素索引的有效方法?
Is pointer-difference a valid way to find the index of an element in a vector within a range-based for loop?
在基于范围的 for 循环中使用指针差来查找元素的索引是否有效?
这里问了很多关于一起使用索引和基于范围的循环的问题,但他们几乎都说如果您还需要元素的索引,则不要使用基于范围的循环。但看起来,至少对于 std::vector
、std::array
和其他在内存中连续的容器,您可以使用指针差异来查找元素的索引,前提是您要遍历元素引用.例如:
// Prints the indices of all elements for which Foo::bar is true.
void Example(const std::vector<Foo>& v) {
for (const auto& e : v) {
if (e.bar()) {
auto index = &e - v.data(); // Valid???
std::cout << index << std::endl;
}
}
}
上面的代码可以正确编译和运行,但我不能完全确定它的有效性。任何语言律师都可以确认或否认这是否是一种可以接受的查找元素索引的方法?
此外,如果容器有一个 data()
成员,那么它的元素在内存中是连续的,因此使用这种方法是安全的吗?
如果底层迭代器满足 LegacyContiguousIterator (C++17) 的要求,那么是的。此要求表明 *(itr + n)
等同于 *(addressof(*itr)+n)
.
这是来自https://en.cppreference.com/w/cpp/named_req/ContiguousIterator
C++20 将其替换为 contiguous_iterator 概念。
Cppreference 页面表明vector<bool>
不符合上述概念,但所有其他向量都符合。与字符串、string_view、数组和 valarray 的 begin/end 重载的迭代器一样。
auto index = &e - v.data(); // Valid???
除非Foo
是bool
的别名,是。
&e
和 v.data()
具有指针类型,[expr.add]/5
适用:
When two pointer expressions P
and Q
are subtracted, the type of the result is an implementation-defined signed integral type; this type shall be the same type that is defined as std::ptrdiff_t
in the <cstddef>
header ([support.types.layout]
).
(5.2)
Otherwise, if P
and Q
point to, respectively, array elements i
and j
of the same array object x
, the expression P - Q
has the value i−j
.
因此,除非 e
或 v.data()
不是同一数组对象的一部分,否则这是明确定义的。如果 Foo
是 bool
的别名,则可能会发生这种情况,否则根据 vector.overview/2
ensuring [container.requirements.general]
, itself ensuring [iterator.concept.contiguous]/2
:
满足此条件
Let a
and b
be dereferenceable iterators and c
be a non-dereferenceable iterator of type I
such that b
is reachable from a
and c
is reachable from b
, and let D
be iter_difference_t<I>
.
The type I
models contiguous_iterator only if
(2.2) to_address(b) == to_address(a) + D(b - a)
, and
(2.3) to_address(c) == to_address(a) + D(c - a)
.
In addition, is it safe to assume that if a container has a data() member, then its elements are contiguous in memory, and is therefore safe to use with this approach?
我们可以构建不安全的奇怪情况,但除非你做一些非常奇特的事情,否则你是安全的!
在基于范围的 for 循环中使用指针差来查找元素的索引是否有效?
这里问了很多关于一起使用索引和基于范围的循环的问题,但他们几乎都说如果您还需要元素的索引,则不要使用基于范围的循环。但看起来,至少对于 std::vector
、std::array
和其他在内存中连续的容器,您可以使用指针差异来查找元素的索引,前提是您要遍历元素引用.例如:
// Prints the indices of all elements for which Foo::bar is true.
void Example(const std::vector<Foo>& v) {
for (const auto& e : v) {
if (e.bar()) {
auto index = &e - v.data(); // Valid???
std::cout << index << std::endl;
}
}
}
上面的代码可以正确编译和运行,但我不能完全确定它的有效性。任何语言律师都可以确认或否认这是否是一种可以接受的查找元素索引的方法?
此外,如果容器有一个 data()
成员,那么它的元素在内存中是连续的,因此使用这种方法是安全的吗?
如果底层迭代器满足 LegacyContiguousIterator (C++17) 的要求,那么是的。此要求表明 *(itr + n)
等同于 *(addressof(*itr)+n)
.
这是来自https://en.cppreference.com/w/cpp/named_req/ContiguousIterator
C++20 将其替换为 contiguous_iterator 概念。
Cppreference 页面表明vector<bool>
不符合上述概念,但所有其他向量都符合。与字符串、string_view、数组和 valarray 的 begin/end 重载的迭代器一样。
auto index = &e - v.data(); // Valid???
除非Foo
是bool
的别名,是。
&e
和 v.data()
具有指针类型,[expr.add]/5
适用:
When two pointer expressions
P
andQ
are subtracted, the type of the result is an implementation-defined signed integral type; this type shall be the same type that is defined asstd::ptrdiff_t
in the<cstddef>
header ([support.types.layout]
).(5.2) Otherwise, if
P
andQ
point to, respectively, array elementsi
andj
of the same array objectx
, the expressionP - Q
has the valuei−j
.
因此,除非 e
或 v.data()
不是同一数组对象的一部分,否则这是明确定义的。如果 Foo
是 bool
的别名,则可能会发生这种情况,否则根据 vector.overview/2
ensuring [container.requirements.general]
, itself ensuring [iterator.concept.contiguous]/2
:
Let
a
andb
be dereferenceable iterators andc
be a non-dereferenceable iterator of typeI
such thatb
is reachable froma
andc
is reachable fromb
, and letD
beiter_difference_t<I>
. The typeI
models contiguous_iterator only if(2.2)
to_address(b) == to_address(a) + D(b - a)
, and
(2.3)to_address(c) == to_address(a) + D(c - a)
.
In addition, is it safe to assume that if a container has a data() member, then its elements are contiguous in memory, and is therefore safe to use with this approach?
我们可以构建不安全的奇怪情况,但除非你做一些非常奇特的事情,否则你是安全的!