簡體   English   中英

自制迭代器的正確性

[英]Const-correctness of self made iterators

總目標

我管理對象的集合( CollectionReal簡單的例子)。 然后我在我的集​​合上定義了迭代器。 這意味着: iteratorconst_iteratorreverse_iteratorconst_reverse_iterator 在這個例子中,我只關注iteratorconst_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; }
};

在過濾器中實現迭代器之前,我有一些評論/問題。

  1. 這個過濾器適用於非const Collection& ,那么,是否真的需要begin() constend() const函數? 如果是,為什么?
  2. 我不能在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

一些問題:

  1. 我對這種方法完全錯了嗎?
  2. 我想我必須復制CollectionFilter::iterator的代碼來實現CollectionFilter::const_iterator ,只做很小的修改。 有沒有辦法避免重復此代碼(寫入8次,如果我計算重復的類CollectionFilterConst和反向迭代器)?
  3. 我對代碼的const正確性感到不舒服。 你看到一些問題嗎?

提前致謝 !

  1. 這個過濾器適用於非const Collection& ,那么,是否真的需要begin() constend() const函數? 如果是,為什么?
  2. 我不能在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_iteratorCollection::iteratorFilter<const Collection>::collection_iteratorCollection::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_iteratorCollection::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::vectorstd::mapstd::string等)擁有它們的內容對象:它們創建和刪除它們,並且需要確保使用const訪問容器,您也只能獲得對內容對象的const訪問權限。 需要實現它們以強制執行此操作,因為它們訪問其分配的存儲的基礎指針沒有這種所有權概念。 這就是為什么他們需要有兩個版本的迭代器( iteratorconst_iterator )。 迭代器本身不擁有該對象:使用const訪問iterator ,您無法推進迭代器,但仍然可以獲得對該對象的非const訪問。

問題1/2: CollectionFilter是有問題的,因為它不擁有它提供訪問的對象,但是對過濾器的const / non-const訪問應該只給對象的const / non-const訪問。 因為它擁有的一個參考Collection ,它應該為const和到非const訪問工作Collection ,兩個版本CollectionFilterConstCollectionFilter然后將需要用這種方法。

問題4:一旦從const-correct容器對象拆分為const和非const訪問的兩個類,就必然會有一些代碼重復。 模板避免必須手動實現兩個版本。 還有一些額外的復雜功能,例如將iteratorconst_iterator進行比較,以及從iterator構造const_iterator ,而不是相反...

問題3/5:見上文。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM