前提
说我有一个容器类Box
,它提供内部类const_iterator
和iterator
。因为我希望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());
向编译错误问好:
no member named 'begin' in 'Box::const_iterator'
‘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_*
的参数具有iterator
和const_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::iterator
的const_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_iterator
的Box::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_iterator
和iterator
的成员(也称为构造函数)。同样,使iterator
继承自const_iterator
并不是const正确的,因为const_iterator
应该只保存一个指针/类似于const
数据。 STL容器还将两个迭代器分开。在该代码中,只需定义简单的定义即可,而不是定义迭代器类
using iterator = int*;
using const_iterator = const int*;
关于c++ - 如何使我的迭代器类看起来不像容器类?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26347052/