[英]Const-correctness of self made iterators
我管理對象的集合( Collection
的Real
簡單的例子)。 然后我在我的集合上定義了迭代器。 這意味着: iterator
, const_iterator
, reverse_iterator
和const_reverse_iterator
。 在這個例子中,我只關注iterator
和const_iterator
,其他兩個非常相似。
之后,我想在我的集合中定義一個過濾器,它根據特定條件保留或不保留元素。 例如,僅保留具有正值的Real
實例。 我也想在我保留的元素上迭代我的集合。
對於此示例,集合中的對象非常簡單。 目標只是擁有一個對象而不是一個本機類型:
struct Real
{
public:
double r;
};
然后我定義我的集合而不必知道里面的真實容器:
class Collection
{
public:
typedef std::vector<Real>::iterator iterator;
typedef std::vector<Real>::const_iterator const_iterator;
private:
std::vector<Real> data;
public:
Collection() : data() {}
Collection(unsigned long int n) : data(n) {}
Collection(unsigned long int n, const Real& x) : data(n,x) {}
Collection::iterator begin() { return this->data.begin(); }
Collection::iterator end() { return this->data.end(); }
Collection::const_iterator begin() const { return this->data.begin(); }
Collection::const_iterator end() const { return this->data.end(); }
};
在這個簡單的例子中,這非常有效:
int main()
{
Collection c(5);
double k = 1.0;
for(Collection::iterator it = c.begin(); it != c.end(); ++it)
{
it->r = k;
k *= -2.0;
}
std::cout << "print c with Collection::iterator" << std::endl;
for(Collection::iterator it = c.begin(); it != c.end(); ++it)
std::cout << it->r << std::endl;
std::cout << "print c with Collection::const_iterator" << std::endl;
for(Collection::const_iterator it = c.begin(); it != c.end(); ++it)
std::cout << it->r << std::endl;
return 0;
}
此程序寫入預期的輸出:
print with Collection::iterator
1
-2
4
-8
16
print with Collection::const_iterator
1
-2
4
-8
16
現在我想創建一個抽象過濾器,它具有一個引用或指向集合的指針,具有迭代器,並具有通過過濾器接受值的抽象函數。 對於第一步,我只編寫沒有迭代器的類:
class CollectionFilter
{
private:
Collection& col;
public:
CollectionFilter(Collection& c) : col(c) {}
virtual ~CollectionFilter() {}
Collection& collection() { return this->col; }
iterator begin() { /* todo */ }
iterator end() { /* todo */ }
const_iterator begin() const { /* todo */ }
const_iterator end() const { /* todo */ }
virtual bool accept(const Real& x) const = 0;
};
然后,創建一個實現特定條件的新過濾器非常容易:
class CollectionFilterPositive : public CollectionFilter
{
public:
CollectionFilterPositive(Collection& c) : CollectionFilter(c) {}
virtual ~CollectionFilterPositive() {}
virtual bool accept(const Real& x) const { return x.r >= 0.0; }
};
在過濾器中實現迭代器之前,我有一些評論/問題。
Collection&
,那么,是否真的需要begin() const
和end() const
函數? 如果是,為什么? const Collection&
上應用過濾器,但顯然需要我的目標。 有什么好辦法呢? 我是否要將類CollectionFilter
復制到類CollectionFilterConst
並使用非常相似的代碼? 此外,對於必須從兩個類似類繼承的用戶來說,這種解決方案非常混亂。 然后,讓我們去實現迭代器。 對於這個例子,我只寫了iterator
而不是const_iterator
。 我把它添加到我的班級:
class CollectionFilter
{
public:
class iterator
{
private:
CollectionFilter* filter;
Collection::iterator iter;
public:
iterator(CollectionFilter* f, Collection::iterator i) : filter(f), iter(i) {}
iterator(const iterator& i) : filter(i.filter), iter(i.iter) {}
iterator& operator = (const iterator& i) { this->filter = i.filter; this->iter = i.iter; return *this; }
iterator& operator ++ ()
{
if(this->iter != this->filter->collection().end())
{
do
{
++this->iter;
} while(this->iter != this->filter->collection().end() && !this->filter->accept(*this->iter));
}
}
iterator operator ++ (int) { /* similar */ }
Real& operator * () { return *this->iter; }
Collection::iterator operator -> () { return this->iter; }
bool operator == (const iterator& i) const { return this->iter == i.iter; }
bool operator != (const iterator& i) const { return this->iter != i.iter; }
};
public:
iterator begin()
{
Collection::iterator it = this->col.begin();
if(!this->accept(*it)) ++it;
return CollectionFilter::iterator(this,it);
}
iterator end()
{
Collection::iterator it = this->col.end();
return CollectionFilter::iterator(this,it);
}
};
這也適用於這個簡單的例子
int main()
{
Collection c(5);
double k = 1.0;
for(Collection::iterator it = c.begin(); it != c.end(); ++it)
{
it->r = k;
k *= -2.0;
}
std::cout << "print c with CollectionFilterPositive::iterator" << std::endl;
CollectionFilterPositive fc(c);
for(CollectionFilterPositive::iterator it = fc.begin(); it != fc.end(); ++it)
std::cout << it->r << std::endl;
return 0;
}
給出預期的輸出:
print with CollectionFilterPositive::iterator
1
4
16
一些問題:
CollectionFilter::iterator
的代碼來實現CollectionFilter::const_iterator
,只做很小的修改。 有沒有辦法避免重復此代碼(寫入8次,如果我計算重復的類CollectionFilterConst
和反向迭代器)? 提前致謝 !
- 這個過濾器適用於非
const
Collection&
,那么,是否真的需要begin() const
和end() const
函數? 如果是,為什么?- 我不能在
const Collection&
上應用過濾器,但顯然需要我的目標。 有什么好辦法呢? 我是否要將class CollectionFilter
復制到class CollectionFilterConst
並使用非常相似的代碼? 此外,對於必須從兩個類似類繼承的用戶來說,這種解決方案非常混亂。
這些問題非常相關。 基本上,將過濾限制為非const Collection
是否有意義? 這對我來說並沒有太大的影響。 我們根本不修改CollectionFilter
對象,只修改底層的Collection
對象(可能), Filter
的功能與Collection
是否為const
無關。 把它們放在一起,它需要一個模板:
template <typename C>
class Filter {
static_assert(std::is_same<
std::decay_t<C>,
Collection
>::value, "Can only filter a Collection.");
using collection_iterator = decltype(std::declval<C&>().begin());
C& collection_;
public:
Filter(C& collection) : collection_(collection) { }
struct iterator {
/* TODO, use collection_iterator */
};
iterator begin() const { /* TODO */ };
iterator end() const { /* TODO */ };
};
這樣, Filter<Collection>::collection_iterator
是Collection::iterator
, Filter<const Collection>::collection_iterator
是Collection::const_iterator
。 你不能做Filter<std::vector<int>>
。
這種類型的答案你的問題,其余還有-這是一個const
-correct,非重復的方式來過濾任何集合。
為了避免額外輸入,您還可以創建構建器功能:
template <typename <typename> class F, typename C>
F<C> makeFilter(C& collection) {
return F<C>(collection);
}
auto filter = makeFilter<CollectionFilterPositive>(some_collection);
該const
的迭代器-ness filter
將取決於const
的-ness some_collection
。
我還會研究Boost.IteratorFacade來編寫Filter::iterator
,它會為你節省一些時間和一些麻煩。
我建議刪除CollectionFilter
類,而是有一個Collection::filter_iterator_tmpl
模板類,有兩個實例Collection::filter_iterator
和Collection::const_filter_iterator
。
Collection::filter_iterator_tmpl
可以像這樣實現:
class Collection {
template<typename Iterator, typename Predicate>
class filter_iterator_tmpl :
public std::iterator<std::input_iterator_tag, typename Iterator::value_type, typename Iterator::difference_type, typename Iterator::pointer, typename Iterator::reference> {
private:
Iterator underlying_iterator_;
Predicate predicate_;
public:
filter_iterator_tmpl& operator++() {
do {
++ underlying_iterator_;
} while(! predicate_(*underlying_iterator_));
return *this;
}
typename Iterator::reference operator*() const {
return *underlying_iterator_;
}
....
}
};
可以通過讓Predicate
成為具有virtual bool PolymorphicPredicate::operator(Real) const
函數的多態函數來添加多態性。
然后Collection
將定義實際的過濾器迭代器:
class Collection {
private:
template<typename Iterator, typename Predicate>
class filter_iterator_tmpl;
public:
template<typename Predicate>
using filter_iterator = filter_iterator_tmpl<Collection::iterator, Predicate>;
template<typename Predicate>
using const_filter_iterator = filter_iterator_tmpl<Collection::const_iterator, Predicate>;
template<typename Predicate>
filter_iterator<Predicate> begin_filter(const Predicate& pred);
template<typename Predicate>
const_filter_iterator<Predicate> begin_filter(const Predicate& pred) const;
}
Boost以類似的方式實現了一個通用的“Filter Iterator”: http : //www.boost.org/doc/libs/1_46_1/libs/iterator/doc/filter_iterator.html作為一個獨立的類,而不是作為容器類的一部分。
關於const-correctness:C ++中的容器( std::vector
, std::map
, std::string
等)擁有它們的內容對象:它們創建和刪除它們,並且需要確保使用const訪問容器,您也只能獲得對內容對象的const訪問權限。 需要實現它們以強制執行此操作,因為它們訪問其分配的存儲的基礎指針沒有這種所有權概念。 這就是為什么他們需要有兩個版本的迭代器( iterator
和const_iterator
)。 迭代器本身不擁有該對象:使用const訪問iterator
,您無法推進迭代器,但仍然可以獲得對該對象的非const訪問。
問題1/2: CollectionFilter
是有問題的,因為它不擁有它提供訪問的對象,但是對過濾器的const / non-const訪問應該只給對象的const / non-const訪問。 因為它擁有的一個參考Collection
,它應該為const和到非const訪問工作Collection
,兩個版本CollectionFilter
和ConstCollectionFilter
然后將需要用這種方法。
問題4:一旦從const-correct容器對象拆分為const和非const訪問的兩個類,就必然會有一些代碼重復。 模板避免必須手動實現兩個版本。 還有一些額外的復雜功能,例如將iterator
與const_iterator
進行比較,以及從iterator
構造const_iterator
,而不是相反...
問題3/5:見上文。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.