簡體   English   中英

如何在沒有容器的迭代器中創建operator->?

[英]How to create operator-> in iterator without a container?

template <class Enum>
class EnumIterator {
 public:

  const Enum* operator-> () const {
    return &(Enum::OfInt(i));  // warning: taking address of temporary
  }

  const Enum operator* () const {
    return Enum::OfInt(i);     // There is no problem with this one!
  }

 private:
  int i;
};

我在上面收到此警告。 目前,我正在使用此hack:

template <class Enum>
class EnumIterator {
 public:
  const Enum* operator-> () {
    tmp = Enum::OfInt(i);
    return &tmp;
  }
 private:
  int i;
  Enum tmp;
};

但這很丑陋,因為迭代器充當丟失的容器。

在值范圍內進行迭代的正確方法是什么?

更新:迭代器專用於特定的集合對象,這些對象支持名為靜態構造函數OfInt(更新了代碼段)。

請不要挑剔我粘貼的代碼,而只是要求澄清。 我試圖提取一個簡單的片段。

如果您想知道T將是強枚舉類型(本質上是將int打包到一個類中)。 將有typedef EnumIterator <EnumX> Iterator; 在EnumX類中。

更新2:添加了const,以指示將通過->訪問的強枚舉類的成員不會更改返回的臨時枚舉。

使用operator *更新了代碼,這沒有問題。

迭代器的種類很多。

例如,在向量上,迭代器通常是普通指針:

template <class T>
class Iterator
{
public:
  T* operator->() { return m_pointer; }

private:
  T* m_pointer;
};

但這是可行的,因為向量實際上只是一個數組。

在雙鏈列表中,它會有所不同,該列表將由節點組成。

template <class T>
struct Node
{
  Node* m_prev;
  Node* m_next;
  T m_value;
};

template <class T>
class Iterator
{
public:
  T* operator->() { return m_node->m_value; }

private:
  Node<T>* m_node;
};

通常,您希望迭代器盡可能輕巧,因為它們是按值傳遞的,所以指向基礎容器的指針很有意義。

您可能需要添加其他調試功能:

  • 使迭代器無效的可能性
  • 范圍檢查的可能性
  • 容器檢查(即,在比較2個迭代器時,首先要檢查它們引用的是同一容器)

但是,這些都是很好的東西,首先,這要復雜一些。

還請注意Boost.Iterator ,它有助於樣板代碼。

編輯:(將更新1和2分組)

在您的情況下,如果您的迭代器只是一個int,就不需要更多,那很好。 實際上,對於強大的枚舉,您甚至不需要迭代器,只需要operator ++和operator-- :)

引用容器的重點通常是實現那些++和-運算符。 但是從您的元素來看,只需要有一個int(假設它足夠大),以及一種獲取上一個和下一個值的方法就足夠了。

但是,如果您有一個靜態向量,那會更容易,那么您可以簡單地重用向量迭代器。

Enum* operator-> () {
  tmp = Enum::OfInt(i);
  return &tmp;
}

問題不在於丑陋,而是不安全。 發生什么情況,例如在如下代碼中:

void f(EnumIterator it)
{
   g(*it, *it);
}

現在g()以兩個指針結尾,這兩個指針都指向同一個內部臨時對象,該內部臨時對象應該是迭代器的實現細節。 如果g()通過一個指針寫入,則另一個值也將更改。 哎喲。

您的問題是,該函數應該返回一個指針,但是您沒有指向的對象。 無論如何,您都必須解決此問題。

我看到兩種可能性:

  1. 由於這東西似乎包裝了一個enum ,並且枚舉類型沒有成員,所以operator->無論如何都是沒用的(除非被調用,否則它不會被實例化,並且不能被調用,因為這會導致編譯時錯誤)並且可以安全地省略。
  2. 正確類型的對象(類似於Enum::enum_type )存儲在迭代器中,並且僅當您要在其上執行類似於整數的操作(例如,增量)時,才將其Enum::enum_type轉換為int或從int Enum::enum_type

迭代器在特定的容器上進行迭代。 具體實現取決於它是哪種容器。 您返回的指針應指向該容器的成員。 您不需要復制它,但是您需要跟蹤要在哪個容器上進行迭代以及大概在迭代器的構造函數中初始化過的位置(例如,向量的索引)。 或者只是使用STL。

OfInt返回什么? 在這種情況下,它似乎返回了錯誤的類型。 它應該返回一個T *,而不是它似乎返回一個按值的T,然后您將其取為地址。 這可能會產生錯誤的行為,因為它會丟失通過->進行的所有更新。

由於沒有容器,因此我決定將迭代器合並到強大的Enum中。 我將原始int初始化為-1以支持空的枚舉(限制== 0),並且能夠對TryInc使用常規的for循環。

這是代碼:

template <uint limit>
class Enum {
 public:
  static const uint kLimit = limit;

  Enum () : raw (-1) {
  }

  bool TryInc () {
    if (raw+1 < kLimit) {
      raw += 1;
      return true;
    }
    return false;
  }

  uint GetRaw() const {
    return raw;
  }

  void SetRaw (uint raw) {
    this->raw = raw;
  }

  static Enum OfRaw (uint raw) {
    return Enum (raw);
  }

  bool operator == (const Enum& other) const {
    return this->raw == other.raw;
  }

  bool operator != (const Enum& other) const {
    return this->raw != other.raw;
  }

 protected:
  explicit Enum (uint raw) : raw (raw) {
  }

 private:
  uint raw;
};

用法:

class Color : public Enum <10> {
 public:
  static const Color red;

  // constructors should be automatically forwarded ...
  Color () : Enum<10> () {
  }

 private:

  Color (uint raw) : Enum<10> (raw) {
  }
};

const Color Color::red = Color(0);


int main() {
  Color red = Color::red;

  for (Color c; c.TryInc();) {
    std::cout << c.GetRaw() << std::endl;
  }
}

暫無
暫無

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

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