[英]Can raw pointers be used instead of iterators with STL algorithms for containers with linear storage?
I have a custom vector container that internally stores item a linear array.我有一个自定义向量容器,它在内部存储一个线性数组项。 Last night, I was trying to implement custom iterators for my class to be able to use them with STL algorithms.
昨晚,我试图为我的类实现自定义迭代器,以便能够将它们与 STL 算法一起使用。 I have had some success that you can see in here:
我已经取得了一些成功,你可以在这里看到:
Live example with custom iterators带有自定义迭代器的实时示例
While doing so, I discovered I can merely pass raw pointers to STL algorithm and they just seem to work fine.这样做时,我发现我只能将原始指针传递给 STL 算法,而且它们似乎工作正常。 Here's the example without any iterators:
这是没有任何迭代器的示例:
#include <cstddef>
#include <iostream>
#include <iterator>
#include <algorithm>
template<typename T>
class my_array{
T* data_;
std::size_t size_;
public:
my_array()
: data_(NULL), size_(0)
{}
my_array(std::size_t size)
: data_(new T[size]), size_(size)
{}
my_array(const my_array<T>& other){
size_ = other.size_;
data_ = new T[size_];
for (std::size_t i = 0; i<size_; i++)
data_[i] = other.data_[i];
}
my_array(const T* first, const T* last){
size_ = last - first;
data_ = new T[size_];
for (std::size_t i = 0; i<size_; i++)
data_[i] = first[i];
}
~my_array(){
delete [] data_;
}
const my_array<T>& operator=(const my_array<T>& other){
size_ = other.size_;
data_ = new T[size_];
for (std::size_t i = 0; i<size_; i++)
data_[i] = other.data_[i];
return other;
}
const T& operator[](std::size_t idx) const {return data_[idx];}
T& operator[](std::size_t& idx) {return data_[idx];}
std::size_t size(){return size_;}
T* begin(){return data_;}
T* end(){return data_+size_;}
};
template<typename T>
void print(T t) {
std::cout << t << std::endl;
}
int main(){
typedef float scalar_t;
scalar_t list [] = {1, 3, 5, 2, 4, 3, 5, 10, 10};
my_array<scalar_t> a(list, list+sizeof(list)/sizeof(scalar_t));
// works!
for (scalar_t* it = a.begin(), *end = a.end();
it != end; ++it)
std::cout << ' ' << *it;
std::cout << std::endl;
// works!
std::for_each(a.begin(), a.end(), print<scalar_t>);
std::cout << std::endl;
// works!
my_array<int> b(a.size());
std::copy(a.begin(), a.end(), b.begin());
// works!
scalar_t* end = std::remove(a.begin(), a.end(), 5);
std::for_each(a.begin(), end, print<scalar_t>);
std::cout << std::endl;
// works!
std::random_shuffle(a.begin(), end);
std::for_each(a.begin(), end, print<scalar_t>);
std::cout << std::endl;
// works!
std::cout << "Counts of 3 in array = " << std::count(a.begin(), end, 3) << std::endl << std::endl;
// works!
std::sort(a.begin(), end);
std::for_each(a.begin(), end, print<scalar_t>);
std::cout << std::endl;
// works!
if (!std::binary_search(a.begin(), a.end(), 5))
std::cout << "Removed!" << std::endl;
return 0;
}
Live example without iterators没有迭代器的实时示例
My questions here are the following:我的问题如下:
One of the features of iterators being based on operator-overloading, is that pointers are already random-access iterators.基于运算符重载的迭代器的特征之一是指针已经是随机访问迭代器。 This was a big design win in the early days of STL, as it made it easier to use algorithms with existing code (as well as making the interface more familiar to programmers).
这是 STL 早期的一个重大设计胜利,因为它可以更轻松地将算法与现有代码一起使用(并使程序员更熟悉界面)。 It's perfectly reasonable to wrap an array, add
typedef T* iterator; typedef const T* const_iterator
包装一个数组,添加
typedef T* iterator; typedef const T* const_iterator
是完全合理的typedef T* iterator; typedef const T* const_iterator
typedef T* iterator; typedef const T* const_iterator
, return &array[0]
from your begin()
and &array[size]
from your end()
, and then use your container with any iterator-based algorithm. typedef T* iterator; typedef const T* const_iterator
,返回&array[0]
从begin()
和&array[size]
从end()
然后使用您的容器与任何基于迭代器的算法。 As you already realise, this will work for any container where elements are contiguous in memory (such as an array).正如您已经意识到的,这适用于任何元素在内存中是连续的容器(例如数组)。
You might implement 'real' iterators if:如果出现以下情况,您可能会实现“真正的”迭代器:
T*
to a my_array::iterator
.T*
分配给my_array::iterator
。 I'd say this last advantage alone is well worth writing a trivial wrapper class for.我想说仅此最后一个优势就值得为其编写一个简单的包装类。 If you don't take advantage of C++'s type system by making different kinds of thing have different types, you might as well switch to Javascript :-)
如果您没有通过使不同类型的事物具有不同类型来利用 C++ 的类型系统,那么您不妨切换到 Javascript :-)
It happens that pointers provide the interface required of random access iterators (dereference, increment, addition, difference, etc) and can be treated just like iterators.碰巧的是,指针提供了随机访问迭代器(解引用、增量、加法、差值等)所需的接口,并且可以像迭代器一样对待。
T*
to an iterator type this is probably not a significant issue.T*
类型定义为迭代器类型,这可能不是一个重要问题。 Additionally some algorithms may benefit from an iterator that's tagged with the iterator type, which you can't do for simple pointer types.Does this always work for containers that have linear storage?
这是否总是适用于具有线性存储的容器?
Yes, the iterator concepts were designed so that pointers could act as iterators over arrays.是的,迭代器概念的设计是为了让指针可以充当数组上的迭代器。
If they do work in this situation, why should I ever go through the hassle of implementing iterators anyway?
如果它们确实在这种情况下工作,我为什么还要经历实现迭代器的麻烦?
There's no good reason to define your own iterator type in this situation, unless you want to do something like bounds-checking which can't be done with a simple pointer.在这种情况下没有充分的理由定义您自己的迭代器类型,除非您想执行诸如边界检查之类的操作,而这种操作无法通过简单的指针来完成。
One slight benefit would be that you could include nested typedefs for the iterator's traits, as some of the standard iterator types do;一个小小的好处是您可以为迭代器的特征包含嵌套的 typedef,就像一些标准迭代器类型所做的那样; but using pointers these are available from
std::iterator_traits<T*>
anyway.但是无论如何使用指针,这些都可以从
std::iterator_traits<T*>
。
What are the negative issues of what I'm doing if this approach would always work?
如果这种方法总是有效,我正在做的事情有哪些负面问题? For one thing, I can see I'm breaking data encapsulation.
一方面,我可以看到我正在破坏数据封装。
To make the interface more consistent with STL-style containers, you should define iterator
and const_iterator
types ( typedef
aliases for the pointers), and provide const
overloads of begin
and end
;为了使接口与 STL 风格的容器更加一致,您应该定义
iterator
和const_iterator
类型(指针的typedef
别名),并提供begin
和end
const
重载; and perhaps cbegin
and cend
for C++11 compatiblity.也许
cbegin
和cend
为C ++ 11的兼容性。
There are various other requirements that you might want to conform to;您可能希望遵守其他各种要求; see section 23.2 of the C++ standard for the gory details.
有关详细信息,请参阅 C++ 标准的第 23.2 节。 But in general, it's more important to make iterators conform to their requirements, since STL-style algorithms work with iterators rather than containers, and by using pointers you already conform to those requirements.
但总的来说,使迭代器符合它们的要求更为重要,因为 STL 风格的算法使用迭代器而不是容器,并且通过使用指针,您已经符合这些要求。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.