gpt4 book ai didi

c++ - 如何使我的迭代器类看起来不像容器类?

转载 作者:可可西里 更新时间:2023-11-01 16:47:46 27 4
gpt4 key购买 nike

前提

假设我有一个容器类 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:“Box::const_iterator”中没有名为“begin”的成员
  • g++: “const class Box::const_iterator”没有名为“begin”的成员

原因

一些研究使我找到了 this template在 Google 测试源代码中(点击扩展文档的链接):

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 来说都像是容器类!

问题

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

我尝试过的事情:

  • const_iteratorstd::iterator 父类(super class)声明为 private。这通过隐藏 iterator 成员类解决了 const_iterator 的问题,但它仍然不允许我将 a.begin() 作为EXPECT_NE 的参数,除非 aconst。似乎出于某种原因,Google Test 使用 iterator begin() 而不是 const_iterator begin() const
  • 完全删除 std::iterator 父类(super class)。这是一个坏主意吗?我想我必须手动声明我的 std::iterator_traits,如果不扩展 std::iterator 我还会有什么损失吗?
  • Box::iteratorBox::const_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/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com