基于范围的循环不适用于数组的指针

时间:2017-05-11 20:24:02

标签: c++ arrays

为什么基于范围的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;
}

4 个答案:

答案 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::beginstd::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)