前提

说我有一个容器类Box,它提供内部类const_iteratoriterator。因为我希望iterator可转换为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来测试这些类。让我们断言begin()end()不会返回相同的东西:
const Box a;
EXPECT_NE(a.begin(), a.end());

向编译错误问好:
  • clang :no member named 'begin' in 'Box::const_iterator'
  • g++:‘const class Box::const_iterator’ has no member named ‘begin’

  • 原因

    一些研究使我在Google Test源代码中找到this template(请参阅扩展文档的链接):
    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_iterator的成员类,而std::iterator作为名为iterator的成员类。
  • Box::iterator本身作为名为iterator的成员类,而Box::const_iterator作为名为const_iterator的成员类。

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

    问题

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

    我尝试过的事情:
  • 声明std::iteratorconst_iterator父类(super class)为private。这通过隐藏const_iterator成员类解决了iterator的问题,但是除非a.begin()EXPECT_NE,否则它仍然不允许我将a作为参数传递给const。出于某种原因,似乎Google测试使用iterator begin()而不是const_iterator begin() const
  • 完全删除std::iterator父类(super class)。这是一个坏主意吗?我想我必须手动声明我的std::iterator_traits,如果不扩展std::iterator,我还会失去什么吗?
  • 声明Box::const_iteratorBox::iterator父类(super class)为private。这可能是选项,也可能不是选项,因为我不得不重新声明我想重用的方法(例如operator++)。

  • 还有其他我忽略的东西吗?

    这个例子
    #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_iteratoriterator的成员(也称为构造函数)。同样,使iterator继承自const_iterator并不是const正确的,因为const_iterator应该只保存一个指针/类似于const数据。 STL容器还将两个迭代器分开。

    在该代码中,只需定义简单的定义即可,而不是定义迭代器类
    using iterator = int*;
    using const_iterator = const int*;
    

    关于c++ - 如何使我的迭代器类看起来不像容器类?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26347052/

    10-09 05:44