【问题标题】:Does std::iterator::reference have to be a reference?std::iterator::reference 必须是参考吗?
【发布时间】:2020-01-31 20:19:17
【问题描述】:

我已经根据此处的答案实现了一个自定义迭代器:
https://stackoverflow.com/a/31886483/1973454

但是,我的代码最终检索存储在某个奇异位置的值,而不是 Type* _ptr 成员,就好像它正在使用以下函数:

// my iterator class gets its values from this function
float globalFloat{ 0 };
float* retrieveFloat (int idx)
{
    globalFloat = (float)idx;
    return &globalFloat;
}

这意味着为了同时使用两个迭代器(即使用 upper_bound 进行搜索),我必须在允许访问之前在本地缓存浮点数:

class Iterator : public std::iterator<std::random_access_iterator_tag, float, int>
{
public:
    Iterator () = default;
    Iterator (int idx) : _index (idx) {}

    Iterator& operator++ () noexcept { ++_index; return *this; }
    Iterator& operator-- () noexcept { --_index; return *this; }

    /// ... rest of iterator declaration

    const float& operator* () const { _data = *retrieveFloat (_index); return _data; }
    const float* operator-> () const { _data = *retrieveFloat (_index); return &this->_data; }
    const float& operator[] (int offset) const { _data = *retrieveFloat (_index + offset); return _data; }

private:
    int _index{ 0 };
    mutable float _data{ 0 };
};

我担心的是最后一个运算符[]。根据cppreference:
https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator

[] 运算符必须返回一个引用类型。但是,如果我要编写以下代码:

int main (int argc, char ** argv)
{
    Iterator it;
    if (it[0] == it[1])
        return 0;
    return 1;
}

然后我返回 0,因为每个 [] 调用都会修改 _data。

如果我改变我对 std::iterator 的子类化方式并使用 float 作为我的“参考”类型:

class Iterator : public std::iterator<std::random_access_iterator_tag, float, int, float*, float>
{
public:
    /// ... rest of iterator declaration (constructors / operators)

    const float operator* () const { _data = *retrieveFloat (_index); return _data; }
    const float* operator-> () const { _data = *retrieveFloat (_index); return &this->_data; }
    const float operator[] (int offset) const { _data = *retrieveFloat (_index + offset); return _data; }
};

然后一切正常...但不知何故感觉很脏。做这种事情合法吗?

我知道,如果我的数据类型不是浮点数,而是要复制的东西更重,那么就会存在性能问题,但为了论证,假设我只使用浮点数或轻量级 POD。我们还假设我不需要修改被迭代的数据。

感谢您提供的任何帮助,如果我提出这个问题的时间过长,我们深表歉意。如果需要,我可以编辑。

  • 约翰

【问题讨论】:

  • 这感觉你的迭代器并不能满足成为RandomAccessIterator 的所有要求,也许BidirectionalIterator 可能更合适,这种情况你不必担心operator[]
  • 以下也需要工作:it[1] = 1;。所以[]的返回值不能是副本。
  • 我预计您的代码将无法工作,因为例如 std::sort 调用 std::swap 将尝试交换指向相同浮点数的 2 个元素。注意:也许我误解了你的例子。
  • @mynameisjohnj 会提升计数+转换迭代器对您来说是更好的解决方案吗? boost.org/doc/libs/1_72_0/libs/iterator/doc/index.html
  • @NoSenseEtAl 它可能......这值得对不同的迭代器类型进行更多研究。谢谢!

标签: c++ reference iterator


【解决方案1】:

std::iterator::reference 必须是引用吗?

没有。 std::iterator::reference 不需要是引用类型。它只需要与operator* 返回的类型相同,并且需要可转换为value_type

但是,为了成为OuputIterator*r = o 必须是格式正确的,因此reference 必须是引用类型或具有非左值引用限定赋值运算符的类类型。因此,使用float 对非输出迭代器很好,但对输出迭代器则不行。

【讨论】:

  • 感谢您的澄清!正如其他一些用户指出的那样,我已经违反了一些关于修改基础数据的要求(即 it[1] = 1 应该可以工作)。如果我确保以 const 方式使用迭代器(即从不修改基础数据),我是否符合这些要求?也许有一个迭代器标签涵盖了这种情况......即 const_random_access_iterator。无论如何,再次感谢!
  • @mynameisjohnj 不需要标签来指定它是否是输出迭代器。
猜你喜欢
  • 2020-07-29
  • 1970-01-01
  • 2022-12-15
  • 2017-10-29
  • 1970-01-01
  • 2014-11-05
  • 1970-01-01
  • 1970-01-01
  • 2013-05-31
相关资源
最近更新 更多