iterator用法 - iterator c++




公開自定義STL風格迭代的首選方法是什麼? (3)

(另請參閱有沒有一種很好的方法,不要手動為C ++中的自定義類型手寫全部12個必需的Container函數?

對於一個類如

namespace JDanielSmith {
class C
{
    const size_t _size;
    const std::unique_ptr<int[]> _data;

public:
    C(size_t size) : _size(size), _data(new int[size]) {}

    inline const int* get() const noexcept { return _data.get(); }
    inline int* get() noexcept { return _data.get(); }

    size_t size() const noexcept { return _size; }
};
}

什麼是暴露迭代的首選方法? 我應該寫begin() / end() (和cbegin() / cbegin() cend() )成員函數嗎?

const int* cbegin() const {
    return get();
}
const int* cend() const {
    return cbegin() + size();
}

或者這些應該是非會員功能?

const int* cbegin(const C& c) {
    return c.get();
}
const int* cend(const C& c) {
    return cbegin(c) + c.size();
}

應該begin() / end()const和非常量重載?

    const int* begin() const {
        return get();
    }
    int* begin() {
        return get();
    }

還有其他的事情要考慮嗎? 有沒有工具/技術來使這個“容易得到正確”,並減少鍋爐代碼的數量?

一些相關的問題/討論包括:


我建議創建兩套函數 - 成員函數以及非成員函數 - 以實現最大的靈活性。

namespace JDanielSmith {
   class C
   {
      const size_t _size;
      const std::unique_ptr<int[]> _data;

      public:
      C(size_t size) : _size(size), _data(new int[size]) {}

      inline const int* get() const { return _data.get(); }
      inline int* get() { return _data.get(); }

      size_t size() const { return _size; }

      int* begin() { return get(); }
      int* end() { return get() + _size; }
      const int* begin() const { return get(); }
      const int* end() const { return get() + _size; }
      const int* cbegin() const { return get(); }
      const int* cend() const { return get() + _size; }

   };

   int* begin(C& c) { return c.begin(); }
   int* end(C& c) { return c.end(); }
   const int* begin(C const& c) { return c.begin(); }
   const int* end(C const& c) { return c.end(); }
   const int* cbegin(C const& c) { return c.begin(); }
   const int* cend(C const& c) { return c.end(); }
}

如果你想能夠使用C類型的對像作為std::beginstd::endstd::cbeginstd::cend std::cbegin參數,那麼成員函數是必須的。

非成員函數是必要的,如果你想能夠使用類型C像作為參數剛剛beginendcbegincbegin 。 ADL將確保非成員函數將被用於這種用法。

int main()
{
   JDanielSmith::C c1(10);

   {
      // Non-const member functions are found
      auto b = std::begin(c1);
      auto e = std::end(c1);
      for (int i = 0; b != e; ++b, ++i )
      {
         *b = i*10;
      }
   }

   JDanielSmith::C const& c2 = c1;
   {
      // Const member functions are found
      auto b = std::begin(c2);
      auto e = std::end(c2);
      for ( ; b != e; ++b )
      {
         std::cout << *b << std::endl;
      }
   }

   {
      // Non-member functions with const-objects as argument are found
      auto b = begin(c2);
      auto e = end(c2);
      for ( ; b != e; ++b )
      {
         std::cout << *b << std::endl;
      }
   }
}

我會選擇C

這裡的主要問題是std::begin()實際上並不適用於使用ADL查找非成員begin() 。 所以真正的解決方案是寫你自己的:

namespace details {
    using std::begin;

    template <class C>
    constexpr auto adl_begin(C& c) noexcept(noexcept(begin(c)))
        -> decltype(begin(c))
    {
        return begin(c);
    }
}

using details::adl_begin;

現在,如果將begin()作為成員函數或非成員函數編寫,則無關緊要,只需在所有地方使用adl_begin(x) ,它就可以工作。 以及標準容器和原始數組。 這方便了旁邊步驟成員與非成員討論。

是的,如果你想暴露const和非常量訪問,你應該有const和非const重載begin()和朋友。


為了創建一個有效的迭代器,你必須確保std :: iterator_traits是有效的。 這意味著你必須設置迭代器類別。

迭代器應該實現iterator(),iterator(iterator &&),iterator(constator&),operator ==,operator!=,operator ++,operator ++(int),operator *,operator =和operator->。 如果可以的話,添加運算符<和運算符+也是一個好主意(不能總是,例如鍊表)。

template <typename T>
class foo
{
public:

  using value_type = T;
  class iterator 
  { 
  public:
    using value_type = foo::value_type;
    using iterator_category = std::random_access_iterator_tag;
    // or whatever type of iterator you have...
    using pointer = value_type*;
    using reference = value_type&;
    using difference_type = std::ptrdiff_t;

    // ... 
  };

  class const_iterator 
  {
    // ... 
  };

  iterator begin() { /*...*/ }
  iterator end() { /*...*/ }

  const_iterator cbegin() const { /*...*/ }
  const_iterator cend() const { /*...*/ }

  /* ... */
};

請參閱: http//en.cppreference.com/w/cpp/iterator/iterator_traits ,了解更多關於構建有效迭代器所需的信息。 (注意:您還需要某些屬性為有效的“容器”,如.size())

理想情況下,你應該使用成員函數開始和結束,但它不是必需的...你也可以重載std :: begin和std :: end。 如果你不知道如何做,我建議你使用成員函數。

您應該創建begin()const和end()const,但是它應該是cbegin()的別名,與begin()不會一樣。





iterator