如何创建运营商 - >在没有容器的迭代器中?

时间:2009-10-27 14:37:39

标签: c++ pointers iterator

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;
};

我上面得到了这个警告。目前我正在使用这个黑客:

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

但这很难看,因为迭代器是一个缺失的容器。

迭代值范围的正确方法是什么?

更新 迭代器专门用于支持命名静态构造函数OfInt(更新代码片段)的特定集合对象。

请不要挑选我粘贴的代码,但只是要求澄清。我试图提取一个简单的部分。

如果你想知道T将是强enum类型(基本上是一个int打包成一个类)。会有typedef EnumIterator&lt; EnumX&gt;迭代器;内部类EnumX。

更新2: 添加的consts表示将通过 - &gt;访问的强枚举类的成员不要更改返回的临时枚举。

使用operator *更新了代码,没有任何问题。

5 个答案:

答案 0 :(得分:1)

有很多种迭代器。

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

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(假设它足够大),并且获得前一个和下一个值的方法就足够了。

虽然如果你有一个静态向量,那么你可以简单地重用一个向量迭代器。

答案 1 :(得分:1)

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

问题不在于它的丑陋,而在于它不安全。会发生什么,例如在以下代码中:

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

现在g()最终得到两个指针,这两个指针指向同一个内部临时指针,它应该是迭代器的实现细节。如果g()通过一个指针写入,则另一个值也会改变。哎哟。

你的问题是,这个函数应该返回一个指针,但你没有指向的对象。无论如何,你都必须解决这个问题。

我看到两种可能性:

  1. 由于这个东西似乎包裹了enum,并且枚举类型没有成员,所以operator->无论如何都是无用的(除非被调用,否则它不会被实例化,因为它不会被调用导致编译时错误)并且可以安全地省略。
  2. right 类型的对象(类似Enum::enum_type)存储在迭代器中,并且仅当您要执行整数时才将其强制转换为int。它上面的操作(例如,增量)。

答案 2 :(得分:0)

迭代器迭代特定容器。实现取决于它是什么类型的容器。您返回的指针应指向该容器的成员。您不需要复制它,但是您需要跟踪您正在迭代的容器,以及您所处的位置(例如,向量的索引),可能是在迭代器的构造函数中初始化的。或者只使用STL。

答案 3 :(得分:0)

OfInt返回什么?在这种情况下,它似乎返回了错误的类型。它应该返回一个T *而不是它似乎返回一个T值,然后你取的是地址。这可能会产生不正确的行为,因为它会丢失通过 - &gt;。

进行的任何更新

答案 4 :(得分:0)

因为没有容器我决定将迭代器合并到我强大的Enum中。 我将raw int设置为-1以支持空枚举(limit == 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;
  }
}