如何使迭代器类看起来不像容器类?

埃米尔·隆伯格

前提

假设我有一个Box提供内部类const_iterator的容器类iterator因为我希望将aiterator转换为a const_iterator,所以后者继承自前者:

class Box {
  // ...
public:
  class const_iterator : public std::iterator<std::random_access_iterator_tag, const int> { /* ... */ };
  class iterator : public const_iterator { /* ... */ };
  // ...
};

问题

现在,我想使用Google Test测试这些类。让我们断言thebegin()end()不要返回相同的东西:

const Box a;
EXPECT_NE(a.begin(), a.end());

向编译错误问好:

  • 铛: no member named 'begin' in 'Box::const_iterator'
  • g ++: ‘const class Box::const_iterator’ has no member named ‘begin’

原因

一些研究使我在Google Test源代码中找到了该模板(请参阅扩展文档的链接):

typedef int IsContainer;
template <class C>
IsContainer IsContainerTest(int /* dummy */,
                            typename C::iterator* /* it */ = NULL,
                            typename C::const_iterator* /* const_it */ = NULL) {
  return 0;
}

这种模板魔术的结果是,如果要EXPECT_*具有iteratorconst_iterator成员类的参数,则假定该类型为容器类。知道这一点,当期望失败时,Google Test可以打印出易于阅读的报告,这很好。

但是,有一点细节:

// Note that we look for both C::iterator and C::const_iterator.  The
// reason is that C++ injects the name of a class as a member of the
// class itself (e.g. you can refer to class iterator as either
// 'iterator' or 'iterator::iterator').  If we look for C::iterator
// only, for example, we would mistakenly think that a class named
// iterator is an STL container.

所以如果我理解正确的话,这意味着

  • Box::const_iterator有一个名为的成员类const_iteratorstd::iterator一个名为的成员类iterator
  • Box::iterator有一个名为的成员类iteratorBox::const_iterator名为的成员类const_iterator

因此,我的两个迭代器类都看起来像Google Test的容器类!

问题

如何设计迭代器类,使它们看起来不像容器?

我尝试过的事情:

  • 声明asstd::iterator超类通过隐藏成员类解决了问题,但是除非我是is 否则它仍然不允许我作为参数传递似乎是Google Test而非出于某种原因使用const_iteratorprivateconst_iteratoriteratora.begin()EXPECT_NEaconstiterator begin()const_iterator begin() const
  • std::iterator完全删除超类。这是一个坏主意吗?我想我必须std::iterator_traits手动声明我的代码,否则如果不进行扩展,还会丢失其他内容std::iterator吗?
  • 声明asBox::const_iterator超类这可能是选项,也可能不是选项,因为我不得不重新声明我想重用的方法(例如)。Box::iteratorprivateoperator++

还有什么我忽略的吗?


这个例子

#include<iterator>
#include <memory> //unique_ptr<T>
#include <gtest/gtest.h>

class ThreeInts {
  std::unique_ptr<int[]> v;

  public:
  ThreeInts() : v(new int[3]) { v[0] = 0; v[1] = 1; v[2] = 2; };
  ThreeInts(int val) : ThreeInts() { v[0] = val; v[1] = val; v[2] = val; };

  bool operator==(const ThreeInts& other) const {
    return v[0] == other.v[0] && v[1] == other.v[1] && v[2] == other.v[2];
  }

  class const_iterator : public std::iterator<std::random_access_iterator_tag, const int> {
  protected:
    int* p;
  public:
    explicit const_iterator(int* p) : p(p) {}
    const_iterator& operator++() { ++p; return *this; }
    bool operator==(const const_iterator& rhs) const { return p == rhs.p; }
    bool operator!=(const const_iterator& rhs) const { return p != rhs.p; }
    int operator*() const { return *p; }
  };

  class iterator : public const_iterator {
  public:
    explicit iterator(int* p) : const_iterator(p) {}
    int& operator*() const { return *p; }
  };

  iterator begin() { return iterator(v.get()); }
  iterator end() { return iterator(v.get()+3); }
  const_iterator begin() const { return const_iterator(v.get()); }
  const_iterator end() const { return const_iterator(v.get()+3); }
};

TEST(ThreeInts, ThisTestCompilesAndPrettyFailureMessagesAreShown) {
  const ThreeInts a(1), b(2);
  ThreeInts c(1), d(2);
  EXPECT_EQ(a, b);
  EXPECT_EQ(a, c);
  EXPECT_EQ(c, d);
}

TEST(ThreeInts, ThisTestCompilesIfTheStdIteratorParentIsPrivate) {
  const ThreeInts a;
  EXPECT_NE(a.begin(), a.end());
}

TEST(ThreeInts, ThisTestAlsoCompilesIfTheStdIteratorParentIsPrivateButItIsAHassle) {
  ThreeInts a;
  ThreeInts::const_iterator beg = a.begin();
  ThreeInts::const_iterator end = a.end();
  //EXPECT_NE(beg, end); // Compile error unless the std::iterator superclass is private
}

TEST(ThreeInts, ThisTestDoesNotCompileEvenIfTheStdIteratorParentIsPrivate) {
  ThreeInts a;
  //EXPECT_NE(a.begin(), a.end());
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
特伦

ThreeInts::iterator不应继承自ThreeInts::const_iterator,而应分别实现。

class ThreeInts::iterator : public std::iterator< std::random_access_iterator_tag, int> { ... }
class ThreeInts::const_iterator : public std::iterator< std::random_access_iterator_tag, const int> { ... }

问题似乎在于,否则ThreeInts::const_iterator两个成员都具有名为const_iterator和的成员iterator(也称为构造函数)。另外,进行iteratorfrom继承const_iterator不是const正确的,因为const_iterator应当仅持有一个指针/类似于const数据。STL容器还将两个迭代器分开。

在该代码中,只需定义简单的定义即可,而不是定义迭代器类

using iterator = int*;
using const_iterator = const int*;

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

类加载器看起来不像“外部”maven 依赖项

我如何制作看起来不像按钮的Glyphicon按钮

如何让我的菜单按钮看起来不像链接?

Django:看起来不像模块路径

git:看起来不像分支的分支

CSV文件看起来不像表格

如何使大型数据集的聚类热图看起来更好?

看起来很冗余的模块和类名

无业游民的触发器并不像看起来那样简单

Cloud Ubuntu 服务器(Ubuntu 16.04) tar:这看起来不像 tar 存档

我的iPhone模拟器看起来不像其他人的

如何制作一个其构造函数看起来像内置类的构造函数的类?

Spring Security-编码密码看起来不像BCrypt

foreach看起来不像在php中正常工作

BCryptPasswordEncoder-编码的密码看起来不像BCrypt

编码的密码看起来并不像BCrypt

警告:编码的密码看起来不像BCrypt

Capistrano错误tar:这看起来不像tar存档

Ubuntu:这看起来不像tar存档

为什么firebase查询看起来不像列表?

焦油通过管道卷曲:看起来不像焦油档案

Django:m看起来不像模块路径

当我迭代它们时,为什么 Rust 中的迭代器看起来很慢?

为什么我的TCP数据包看起来不像协议分析器的TCP数据包?

为什么GHC类型检查器不像看起来几乎相同的两个函数之一?

Django 在服务器启动时尝试导入模块失败(“看起来不像模块路径”)

为什么LinearLayout instance.getLayoutParams看起来有错误的类?

CSS - 同一个类在其他元素上看起来不同

为什么静态嵌套类看起来有实例字段?