简体   繁体   English

stl::带原始指针的迭代器

[英]stl::iterators with raw pointers

I want to use iterators with C++ arrays, but with raw pointers too.我想对 C++ 数组使用迭代器,但也要使用原始指针。 I can do with a static vector:我可以使用静态向量:

#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);

How can be possible to do it with a raw pointer (maybe a heap allocated vector)?如何使用原始指针(可能是堆分配的向量)来做到这一点? Of course the compiler does not know the size of the data, so this code当然编译器不知道数据的大小,所以这段代码

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

gives

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

Is it possible to make begin() and end() aware of it?是否可以让begin()end()意识到它?

Is it possible to make begin() and end() aware of it? 有可能让begin()和end()意识到它吗?

It's possible to implement std::begin for a pointer, but it is impossible to implement std::end (because as you say, the size is unknown), so it is a bit pointless . 可以为指针实现std::begin ,但实现std::end是不可能的(因为正如你所说,大小未知),所以它有点无意义

However, you don't need either of those to use std::find : 但是,您不需要使用std::find

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

No it is not possible to use std::begin and std::end on a pointer. 不可以在指针上使用std::beginstd::end Unlike an array whose size is part of the type and therefor deducible a pointer does not hold the size of the thing it points to. 与大小是类型的一部分的数组不同,因此指针不能保持它指向的东西的大小。 In your case with a pointer you would have to use 在您使用指针的情况下,您将不得不使用

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

The way to avoid this though is to use std::vector when you are not going to know what the szie will be at compile time. 避免这种情况的方法是使用std::vector当你不知道szie在编译时会是什么时。 It will manage the memory for you and provides begin and end member functions. 它将为您管理内存并提供beginend成员功能。

Here: 这里:

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

you're trying to take the beginning and end of a pointer. 你试图抓住指针的开头和结尾。 Nope! 不!

Instead, you meant: 相反,你的意思是:

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

This is the same whether you use an array, or a std::array , or a std::vector , or a particularly large elephant — to get the bounds of a container, you need the container. 无论你使用数组, std::arraystd::vector ,还是特别大的大象,都可以获得容器的边界,你需要容器​​。

I want to use iterators with C++ arrays, but with raw pointers too. 我想在C ++数组中使用迭代器,但也使用原始指针。

You got it backwards. 你倒退了。 Raw pointers are iterators. 原始指针迭代器。 They iterate over arrays. 他们迭代数组。

You can just use them for all the things you would otherwise accomplish with std::begin and std::end . 您可以将它们用于std::beginstd::end所能完成的所有事情。 Most notably, you can pass them to C++ standard algorithms in <algorithm> . 最值得注意的是,您可以将它们传递给<algorithm> C ++标准算法。

A pointer itself cannot be iterated. 指针本身不能被迭代。 Iterators cannot be iterated. 迭代器不能迭代。

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

Very loosely speaking, a pointer is just a number. 非常宽松地说,指针只是一个数字。 What is the "begin" and "end" of a number? 数字的“开始”和“结束”是什么? This piece of code makes as little sense as the following: 这段代码与以下内容一样没有意义:

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

Is it possible to make begin() and end() aware of it? 有可能让begin()end()意识到它吗?

A pointer may point to the beginning of some array. 指针可以指向某个数组的开头。 But that knowledge must be kept along with the pointer. 但是这些知识必须与指针一起保存。 You need to maintain a size or an end pointer along with a start pointer, and keep all that data together. 您需要维护一个大小或结束指针以及一个开始指针,并将所有数据保存在一起。

That's exactly what std::array and std::vector do for you. 这正是std::arraystd::vector为你做的事情。

When I deal with naked arrays I rely on simple containers to make them compatible with C++'s general treatment of ranges . 当我处理阵列时,我依靠简单的容器来使它们与C ++对范围的一般处理兼容。

For example: 例如:

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

Compile and run 编译并运行

$ 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 

You still have to worry about passing the correct dimension to the constructor, and it will fail miserably if the underlying pointer is deleted, but you had to worry about that anyway. 您仍然需要担心将正确的维度传递给构造函数,如果删除了基础指针,它将会失败,但无论如何您都不得不担心。

While others answers already explains why you should revisit your design I want to state one option, that was not described earlier.虽然其他答案已经解释了为什么你应该重新审视你的设计,但我想说明一个选项,之前没有描述过。

Your compiler diagnosis message almost clearly states that the compiler falls to deduce arguments and that arguments looks exactly like reference to С-style array.您的编译器诊断消息几乎清楚地表明编译器会推断参数,并且该参数看起来与对 С 样式数组的引用完全一样。

Reference to C-style array doesn't decay to pointer, so you can pass reference to C-style array to std::begin and std::end in that particular case as an option:对 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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM