问题
I want to use iterators with C++ arrays, but with raw pointers too. 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?
回答1:
No it is not possible to use std::begin
and std::end
on a pointer. 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. It will manage the memory for you and provides begin
and end
member functions.
回答2:
Is it possible to make begin() and end() aware of it?
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.
However, you don't need either of those to use std::find
:
int* p = std::find(pStart, pStart + SIZE, 5);
回答3:
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.
回答4:
When I deal with naked arrays I rely on simple containers to make them compatible with C++'s general treatment of ranges.
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.
回答5:
I want to use iterators with C++ arrays, but with raw pointers too.
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
. Most notably, you can pass them to C++ standard algorithms in <algorithm>
.
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()
andend()
aware of it?
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.
来源:https://stackoverflow.com/questions/41962903/stliterators-with-raw-pointers