基于范围的动态数组循环?

时间:2013-04-09 14:37:54

标签: c++ arrays c++11 foreach dynamic-arrays

有一个基于范围的for循环,其语法为:

for(auto& i : array)

它适用于常量数组,但不适用于基于指针的动态数组,如

int *array = new int[size];
for(auto& i : array)
   cout<< i << endl;

它提供有关替换失败的错误和警告,例如:

  

错误] C:\ Users \ Siegfred \ Documents \ C-Free \ Temp \ Untitled2.cpp:16:16:错误:没有匹配函数来调用'begin(int *&amp;)'

如何在动态数组中使用这种新语法?

6 个答案:

答案 0 :(得分:23)

要使用基于范围的for-loop ,您必须提供begin()end()成员函数或重载非成员begin()end()函数。 在后一种情况下,您可以将范围包装在std::pair中,并将begin()end()重叠:

    namespace std {
        template <typename T> T* begin(std::pair<T*, T*> const& p)
        { return p.first; }
        template <typename T> T* end(std::pair<T*, T*> const& p)
        { return p.second; }
    }

现在你可以像这样使用for循环:

    for (auto&& i : std::make_pair(array, array + size))
        cout << i << endl;

请注意,非成员begin()end()函数必须在std命名空间中重载,因为pair也位于名称空间{{1}中}。如果您不想篡改标准命名空间,只需创建自己的小配对类,并在命名空间中重载stdbegin()

或者,在动态分配的数组周围创建一个瘦包装器,并提供end()begin()成员函数:

end()

您的通话网站如下所示:

    template <typename T>
    struct wrapped_array {
        wrapped_array(T* first, T* last) : begin_ {first}, end_ {last} {}
        wrapped_array(T* first, std::ptrdiff_t size)
            : wrapped_array {first, first + size} {}

        T*  begin() const noexcept { return begin_; }
        T*  end() const noexcept { return end_; }

        T* begin_;
        T* end_;
    };

    template <typename T>
    wrapped_array<T> wrap_array(T* first, std::ptrdiff_t size) noexcept
    { return {first, size}; }

Example

答案 1 :(得分:17)

您不能将range-for-loop与动态分配的数组一起使用,因为编译器无法推断出此数组的开始和结束。您应该始终使用容器而不是容器,例如std::vector

std::vector<int> v(size);
for(const auto& elem: v)
    // do something

答案 2 :(得分:10)

您无法直接在动态分配的数组上执行基于范围的循环,因为您只拥有指向第一个元素的指针。没有关于其大小的信息,编译器可以使用它来执行循环。惯用的C ++解决方案是用std::vector替换动态分配的数组:

std::vector<int> arr(size);
for(const auto& i : arr)
  std::cout<< i << std::endl;

或者,您可以使用基于指针和偏移量提供开始和结束迭代器的范围类型。查看boost.range库中的某些类型,或GSL范围内的提案(示例实现here,参考C ++ 20建议类型here


请注意,基于for循环的范围适用于修复大小普通数组的std::array个对象:

std::array<int,10> arr;
for(const auto& i : arr)
  std::cout<< i << std::endl;

int arr[10] = .... ;
for(const auto& i : arr)
  std::cout<< i << std::endl;

但在这两种情况下,大小都需要是编译时常量。

答案 3 :(得分:1)

C ++ 20将(可能)添加std::span,这允许像这样循环:

#include <iostream>
#include <span>

int main () {
    auto p = new int[5];
    for (auto &v : std::span(p, 5)) {
        v = 1;
    }
    for (auto v : std::span(p, 5)) {
        std::cout << v << '\n';
    }
    delete[] p;
}

不幸的是,截至撰写本文时,目前的编译器似乎还没有支持这一点。

当然,如果你有选择的话,最好从一开始就使用std::vector而不是C风格的数组。

答案 4 :(得分:0)

不是为std::begin的指针定义std::endstd::pair(顺便说一下,在undefined behaviour中在std::中定义它们)并展开了自己的包装,例如suggested before,则可以使用boost::make_iterator_range

size_t size = 16;
int *dynamic_array = new int[size];
for (const auto& i : boost::make_iterator_range(dynamic_array, dynamic_array + size))
    std::cout << i << std::endl;

Live example

答案 5 :(得分:-2)

请参阅此页面http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer并找到“非成员begin()和end()”一章。这可能是你想要实现的目标。