stl::迭代器与裸指针

7

我想在C++数组和裸指针中使用迭代器。 对于静态向量,我可以这样做:

#define SIZE 10
int vect[SIZE] = {0};
vect[3] = 5;
int* p = std::find(std::begin(vect), std::end(vect), 5);
bool success = p != std::end(vect);

如何使用原始指针(可能是堆分配的矢量)来实现它呢?当然,编译器不知道数据的大小,所以这段代码

int* pStart = vect;
std::find(std::begin(pStart), std::end(pStart), 5);

提供

error C2784: '_Ty *std::begin(_Ty (&)[_Size])' : 
could not deduce template argument for '_Ty (&)[_Size]' from 'int *'

是否可以使begin()end()意识到它们之间的元素?


2
如何才能做到“什么”?你想要实现什么目标?你的目标是什么? - Lightness Races in Orbit
2
使用std::array代替,它具有C数组的所有功能,但实际上具有STL接口。而且它的迭代器被实现为指针。 - rlbond
@rlbond 除非是在这种情况下,普通数组也完全可以。 - juanchopanza
6个回答

6

能否使begin()和end()了解它?

对于指针,可以实现std :: begin,但无法实现std :: end(因为如您所说,大小未知),因此有点毫无意义。

但是,您不需要使用这两个函数来使用std :: find:

int* p = std::find(pStart, pStart + SIZE, 5);

1
除非现在你有一个潜在的错误。像这样手动设置边界几乎总是不好的想法。 - Lightness Races in Orbit

5

不可能在指针上使用std::beginstd::end。与数组的大小作为类型的一部分且可推导不同,指针不含有其所指向的对象的大小信息。对于指针,您需要使用以下方法:

std::find(pStart, pStart + SIZE, 5);

如果您在编译时不知道大小,避免这种情况的方法是使用std::vector。它会为您管理内存,并提供beginend成员函数。


您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Stefano Piovesan
@Stefano 我的意思是,如果你正在使用运行时大小,那么你应该首先使用 std::vector。如果不是,则可以使用原始数组或者你可以使用 std::array - NathanOliver
我正在使用C ++数组,因为数据来自共享内存,而数组的大小是通过某种共享内存管理器动态分配的。 - Stefano Piovesan
@Stefano:在这种情况下,共享内存管理器还必须告诉您大小。起始指针和起始指针+大小是迭代器。它们可用于您使用std::beginstd::end完成的所有操作,尤其是在<algorithm>中的所有标准算法。 - Christian Hackl

2

Here:

std::begin(pStart), std::end(pStart)

您想要获取指针的开头和结尾,但这是不正确的操作。

正确的做法是:

std::begin(vect), std::end(vect)

无论您使用数组、std::arraystd::vector还是一个特别大的大象,获取容器的边界都是相同的。在获取容器的边界时,需要使用容器本身。

2
当我处理裸数组时,我依赖于简单的容器使它们与C ++的范围一般处理兼容。
例如:
#include <iostream>
#include <memory>    // for std::unique_ptr
#include <algorithm> // for std::reverse
#include <numeric>   // for std::iota

template<typename T>

class range_view {

 public:

  range_view(T* data, std::size_t size)
      : m_data { data },
        m_size { size } { }

  const T* begin() const { return m_data; }
  const T* end() const { return m_data + m_size; }

  T* begin() { return m_data; }
  T* end() { return m_data + m_size; }

 private:

  T* m_data;
  std::size_t m_size;
};

int main() {

  // this is your actual data
  auto pdata = std::make_unique<int>(20);

  // this is a handle you use to work with your data
  auto data_view = range_view<int>(pdata.get(), 20);

  // for example, you can fill it with numbers 0, ... , N - 1
  std::iota(data_view.begin(), data_view.end(), 0);

  // it is now compatible with c++'s range-based operators
  std::cout << "my data...\n";
  for(auto && item : data_view) std::cout << item << " ";

  std::reverse(data_view.begin(), data_view.end());
  std::cout << "\nreversed...\n";
  for(auto && item : data_view) std::cout << item << " ";
  std::cout << "\n";
}

编译和运行
$ g++ example.cpp -std=c++14
$ ./a.out
my data...
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
reversed...
19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 

你仍然需要担心将正确的尺寸传递给构造函数,如果底层指针被删除,它将失败得很惨,但无论如何你都要担心这个问题。


1

虽然其他答案已经解释了为什么您应该重新审视设计,但我想说明一个之前未被描述的选项。

您的编译器诊断消息几乎清楚地表明编译器无法推断参数,并且参数看起来恰好像是对C风格数组的引用。

C风格数组的引用不会衰减为指针,因此在这种特定情况下,您可以将对C风格数组的引用作为选项传递给std::begin和std::end:

#include <iostream>
#include <algorithm>

constexpr size_t SIZE = 10;

int main() {
    int vect[SIZE] = {0};
    vect[3] = 5;

    int (&ref_to_vect)[SIZE] = vect;    // declaring reference to C-array

        // and using reference to C-array which doesn't decay to pointer opposite to passing array "by value" semantics
    int* p = std::find(std::begin(ref_to_vect), std::end(ref_to_vect), 5);
    bool success = p != std::end(vect);
    std::cout << (success ? "Found 5" : "5 not found :(");

    return 0;
}

0
我想在C++数组中使用迭代器,但也要使用原始指针。
你搞反了。原始指针本身就是迭代器。它们可以遍历数组。
你可以将它们用于所有你本来会用std::beginstd::end完成的事情。最重要的是,你可以将它们传递给<algorithm>中的C++标准算法。
指针本身不能被迭代。迭代器也不能被迭代。
int* pStart = vect;
std::find(std::begin(pStart), std::end(pStart), 5);

大致而言,指针只是一个数字。数字的“开始”和“结束”在哪里?这段代码跟以下内容一样没有意义:

int i = 123;
std::find(std::begin(i), std::end(i), 5); // error

能否让 begin()end() 意识到它呢?

指针可能指向某个数组的开头。但是这个知识必须与指针一起保留。您需要在开始指针、大小或结束指针之间保持一致,并将所有数据放在一起。

这正是 std::arraystd::vector 为您做的事情。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接