为什么基于范围的for循环不适用于数组指针?
auto pCollection = new int[3] { 0,1,2 };
// error C3312: no callable 'begin' function found for type 'int *'
for (auto value : pCollection)
{
std::cout << value << std::endl;
}
delete[] pCollection;
但可以在数组上使用:
int collection[3]{ 0,1,2 };
for (auto value : collection)
{
std::cout << value << std::endl;
}
答案 0 :(得分:7)
指针不是数组。从指针本身无法知道指针指向的位置可能有多少元素。
答案 1 :(得分:4)
假设从函数返回动态分配的数组:
int *pCollection = getCollection();
你如何找到数组的结尾?好吧,你不能 - 指针只指向第一个元素,它不捆绑任何大小的信息。实际上,它可能指向使用int
分配的单个new
,您也不会知道。指针不是容器 - 只是指针。
答案 2 :(得分:2)
auto pCollection = new int[3] { 0,1,2 };
这不是int[3]
。它是一个int*
,指向3 int
的缓冲区。
该类型没有关于其大小的信息。
int collection[3]{ 0,1,2 };
这是int[3]
。它的类型表明它有多大。
这里我们创建了两个不同大小的新数组
auto pCollection = (rand()%2)?new int[3] { 0,1,2 }:new int[5]{1,2,3,4,5};
并存储指向其中一个的指针。 pCollection
的类型不知道它有多大。 C ++中没有办法达到它的大小,并且由于破坏是微不足道的,因此操作系统可以为8个整数提供足够的空间并对额外的空间说“无论什么”。因此,即使访问低级别内存API也可能无法告诉我们它有多大。
见实际int[3]
,
for (auto value : collection) {
std::cout << value << std::endl;
}
此语句可以使用collection
的类型来了解要访问的元素数量。 std::begin
和std::end
被重载以做正确的事情,同样指定for(:)
循环来做正确的事情。
使用int*
时,不存储有关其长度的类型信息。
我们可以自己存储。并提供一种了解它的类型。
这是一个快速的:
template<class It, class R=void>
struct range_t {
It b, e;
It begin() const { return b; }
It end() const { return e; }
range_t(It s, It f):b(std::move(s)), e(std::move(f)) {}
range_t() noexcept(noexcept(It{})) :range_t({},{}) {}
range_t(range_t const&)=default;
range_t(range_t &&)=default;
range_t& operator=(range_t const&)=default;
range_t& operator=(range_t &&)=default;
~range_t()=default;
decltype(auto) front() const { return *begin(); }
decltype(auto) back() const { return *std::prev(end()); }
using own_type = std::conditional_t<
std::is_same<R,void>::value,
range_t,
R
>;
own_type without_front( std::size_t N=1 ) const {
return {std::next(begin(), N), end()};
}
own_type without_back( std::size_t N=1 ) const {
return {begin(), std::prev(end(),N)};
}
std::size_t size() const {
return std::distance( begin(), end() );
}
bool empty() const {
return begin()==end();
}
};
template<class T>
struct span_t:range_t<T*, span_t<T>> {
span_t(T* s, T* f):range_t<T*>(s, f) {}
span_t(T* s, std::size_t l):span_t(s, s+l) {}
T& operator[](std::size_t n)const{ return begin()[n]; }
span_t( range_t<T*> o ):range_t<T*>(o) {}
span_t( span_t const& ) = default;
span_t& operator=( span_t const& ) = default;
span_t() noexcept(true) : span_t(nullptr, nullptr) {}
};
template<class T>
span_t<T> span(T* s, T* f){ return {s,f}; }
template<class T>
span_t<T> span(T* s, std::size_t length){ return {s,length}; }
现在我们可以这样做:
auto pCollection = new int[3] { 0,1,2 };
for (auto value : span(pCollection,3)) {
std::cout << value << std::endl;
}
delete[] pCollection;
鲍勃是你的叔叔。
注意GSL有一个更完整的span<T>
类型。
答案 3 :(得分:1)
要将其变为范围,请使用
boost::make_iterator_range(pCollection, pCollection+3)