从 reverse_iterator 中的用户定义的迭代器继承所有函数

Posted

技术标签:

【中文标题】从 reverse_iterator 中的用户定义的迭代器继承所有函数【英文标题】:Inherit all functions from user-defined iterator in reverse_iterator 【发布时间】:2015-06-24 20:27:39 【问题描述】:

我正在为 C++11 编写 JSON 类,请参阅 http://github.com/nlohmann/json。我的中心数据结构是一个类,将 JSON 值类型(null、array、object、string、bool、number)包装在一个联合中,并通过一个不错的 C++ 接口提供它。由于数组(通过std::vector 实现)和对象(std::map)带有它们自己的迭代器,因此我实现了一个“包装器”迭代器,它将对operator++operator-> 的调用委托给各自的成员变量。此外,我实现了两个附加函数std::string key() 来访问 JSON 对象的键,并实现了 reference value() 作为 operator*() 的别名。

到目前为止,一切都很好(完整的源代码参见https://github.com/nlohmann/json/blob/master/src/json.hpp)...

然后我想实现reverse_iteratorconst_reverse_iterator。问题就从这里开始了。

如果我通过using reverse_iterator = std::reverse_iterator<iterator>;using const_reverse_iterator = std::reverse_iterator<const_iterator>; 实现它们,一切都很好,但是key()value() 函数不适用于reverse_iteratorconst_reverse_iterator 对象。 如果我像class reverse_iterator : public std::reverse_iterator<typename basic_json::iterator> 一样实现我自己的类reverse_iterator,我需要再次实现整个类。仅提供key()value() 的实现是不够的,还提供operator++() 以及我希望使用std::reverse_iterator 适配器免费获得的所有其他内容。

我花了相当长的时间寻找答案,但我发现的所有参考资料要么只是触及了不完整的玩具示例的表面,要么得出的结论是迭代器是一项艰苦的工作,应该转向 Boost...

所以这是我的问题:

    如何从我的自定义类 iterator 创建一个 reverse_iterator 以便它尽可能多地继承功能? 如果继承超出标准内容的行为不能自动工作,我如何编写reverse_iterator 而不完全重复自己?

非常感谢任何帮助!

【问题讨论】:

【参考方案1】:

很遗憾,我没有收到答案,所以这就是我所做的。也许一个丑陋的解决方案会激起某人发布更好的东西:-)

背景

所以我学到的第一件事是std::reverse_iterator 只是封装了一个“普通”迭代器(称为current,可通过base() 访问)并建立反向迭代器与“一个元素”的关系比“正常”迭代器更靠左”。

(图片来自cppreference.com)

通用解决方案

只要“普通”迭代器具有标准接口并且不使用任何附加功能,行

using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;

足以自动将您自己的迭代器类转换为反向迭代器。

和成员函数

reverse_iterator rbegin()  return reverse_iterator(end()); 
reverse_iterator rend()  return reverse_iterator(begin()); 
const_reverse_iterator crbegin() const  return const_reverse_iterator(cend()); 
const_reverse_iterator crend() const  return const_reverse_iterator(cbegin()); 

反向迭代是可能的,例如像这样的代码

for (my_container::reverse_iterator rit = c.rbegin(); rit != c.rend(); ++it)

   // rit will iterator container c in reverse order

有效。

向迭代器添加更多功能

正如我在问题中所写,我用两个额外的成员 key()value() 扩展了 iterator 类。前者允许在迭代期间快速访问 JSON 对象的键。后者是写it.value() 而不是*it 的别名。不幸的是,上述方法并没有将这些函数继承给reverse_iterator

为了丰富用户定义的反向迭代器,我们需要继承 std::reverse_iterator&lt;iterator&gt; 并将调用委托给基迭代器。不幸的是,我发现除了手动执行此操作之外没有其他方法。对于上述功能,如下所示:

class reverse_iterator : public std::reverse_iterator<iterator>

  ...

  std::string key() const
  
      auto it = --this->base();
      return it.key();
  

  reference value() const
  
      auto it = --this->base();
      return it.operator * ();
    

最棘手的部分是您需要手动实现“off-by-one”关系:

    通过base() 检索基迭代器。 将其递减以指向“右”(实际上是左...)元素。 调用所需的函数。

这样,我们就快完成了。几乎,因为上面代码中的...。剩下要做的就是将所有其他对函数(如operator++)的调用委托给基类。我现在找到了让其他人参加这个无聊代表团的方法。

所以类包含类似的代码

using base_iterator = std::reverse_iterator<iterator>;

reverse_iterator operator++(int)

    return base_iterator::operator++(1);


reverse_iterator& operator++()

    base_iterator::operator++();
    return *this;

(注意base_iterator的定义。)

就是这样。我们现在有用户定义的反向迭代器,允许我们编码

for (my_container::reverse_iterator rit = c.rbegin(); rit != c.rend(); ++it)

   std::cout << rit.key() << '\n';

清理

在 Github 上的一次讨论中,gregmarr 提议将reverse_iteratorconst_reverse_iterator 类组合成一个模板类,例如

template<typename Base>
class json_reverse_iterator : public std::reverse_iterator<Base>

  public:
    /// shortcut to the reverse iterator adaptor
    using base_iterator = std::reverse_iterator<Base>;
    /// the reference type for the pointed-to element
    using reference = typename Base::reference;

    /// create reverse iterator from iterator
    json_reverse_iterator(const typename base_iterator::iterator_type& it)
        : base_iterator(it) 

    /// create reverse iterator from base class
    json_reverse_iterator(const base_iterator& it) : base_iterator(it) 

...

这样可以写

using reverse_iterator = json_reverse_iterator<iterator>;
using const_reverse_iterator = json_reverse_iterator<const_iterator>;

要快乐。

完整示例

完整代码见here。

我仍然希望看到一个避免重复大多数功能的解决方案,但这个对我来说已经足够了。由于我很久没有找到更好的东西,所以我决定分享它。

【讨论】:

以上是关于从 reverse_iterator 中的用户定义的迭代器继承所有函数的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 reverse_iterator 插入

使用 reverse_iterator 擦除最后一个元素

C++ rbegin 修改reverse_iterator的地址

对于std :: reverse_iterator c ++,operator!=不明确

奇怪的 std::vector::reverse_iterator 和字符串行为

在遍历中使用 iterator/reverse_iterator 进行 Erase 的用法