28

有一个基于范围的 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*&)'

如何将这种新语法与动态数组一起使用?

4

5 回答 5

27

要使用基于范围的 for 循环,您必须提供begin()andend()成员函数或重载非成员begin()andend()函数。在后一种情况下,您可以将您的范围包装在 astd::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也位于命名空间中std。如果您不想篡改标准命名空间,您可以简单地创建自己的小对类begin()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}; }

您的呼叫站点如下所示:

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

例子

于 2013-04-10T07:42:14.610 回答
18

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

std::vector<int> v(size);
for(const auto& elem: v)
    // do something
于 2013-04-09T14:40:39.400 回答
11

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

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

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


请注意,基于范围的 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;

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

于 2013-04-09T14:48:38.397 回答
5

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;
}

当前编译器支持这一点,例如gcc 10.1clang 7.0.0及更高版本。(直播

当然,如果您可以选择,最好std::vector从一开始就使用 C 样式的数组。

于 2018-05-22T16:12:02.183 回答
1

如前所述std::begin,您可以使用std::end_ std::pair_ std::_ _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;

活生生的例子

于 2018-07-25T00:31:26.257 回答