【问题标题】:Iterator over pure virtual container interface in C++C++中纯虚拟容器接口的迭代器
【发布时间】:2019-06-06 13:09:48
【问题描述】:

我有一个到容器的纯虚拟接口,它或多或少像这样:

class IContainer
{
public:
    virtual ~IContainer() = default;
    virtual Element& operator[](size_t index) = 0;
    virtual const Element& operator[](size_t index) const = 0;
    virtual size_t size() const = 0;
};

我想使用范围 for 循环,所以我需要定义 begin() 和 end()。为此,我还需要定义迭代器类型。

这应该不是特别难,但是在我开始编写已经存在的东西之前,我想知道 STL 或 Boost 中是否已经有任何东西可以提供帮助。

【问题讨论】:

  • 可能是你所追求的:std::iterator.
  • @Aconcagua 但已弃用...
  • 文字题外话,要求推荐第三方库(Boost)
  • @SergeyA 再次,我将其读作“如何使用标准 C++ 和 boost 解决这个问题?”,鉴于 boost 的广泛使用,这似乎是一个合理的问题。但即使 if 你坚持询问如何使用 boost 做某事是在要求一个库(这似乎很可疑,因为“使用 boost.container。”几乎不会回答这个问题),这将如何适合用户界面中明确概述的规则精神?这将如何“吸引固执己见的答案或垃圾邮件”?
  • @SergeyA 鉴于问题是“STL 或 Boost”,您是否会将每个问题标记为“我如何使用 STL 进行 X”?

标签: c++ boost stl iterator containers


【解决方案1】:

这可能不是一个好主意,但在这里添加for(:) 循环支持相对容易。我会是最小的。

我将创建一个iteroid,这是一个足以支持for(:) 循环的非迭代器。这需要++!= 和一元* 支持,仅此而已。

template<class C>
struct index_iteroid {
  decltype(auto) operator*()const {
    return (*container)[i];
  }
  index_iteroid(index_iteroid const&)=default;
  index_iteroid& operator=(index_iteroid const&)=default;
  friend bool operator==(index_iteroid const& lhs, index_iteroid const& rhs) {
    return std::tie(lhs.i, lhs.container)==std::tie(rhs.i, rhs.container);
  }
  friend bool operator!=(index_iteroid const& lhs, index_iteroid const& rhs) {
    return !(lhs==rhs);
  }
  void operator++()&{
    ++i;
  }
  index_iteroid(C* c, std::size_t in):i(in), container(c) {}
private:
  std::size_t i = 0;
  C* container = nullptr;
};

现在我们使用它:

class IContainer
{
public:
    virtual ~IContainer() = default;
    virtual Element& operator[](size_t index) = 0;
    virtual const Element& operator[](size_t index) const = 0;
    virtual size_t size() const = 0;
    index_iteroid<IContainer> begin() { return {this, 0}; }
    index_iteroid<IContainer> end() { return {this, size()}; }
    index_iteroid<IContainer const> begin() const { return {this, 0}; }
    index_iteroid<IContainer const> end() const { return {this, size()}; }
};

你有它。

void test( IContainer* cont ) {
  if (!cont) return;
  for(Element& e : *cont) {
    // code
  }
}

如有错别字请见谅。

现在一个完整的迭代器需要的代码大约是我的 iteroid 的 2-3 倍,但没什么棘手的,主要是烦人的样板代码。


该标准对您没有太多帮助。对于 boost,您可以使用函数校准迭代器/生成器组成一个计数迭代器,并让函数调用使用 []。如果您想将 iteroid 升级为迭代器,Boost 还有一些实用程序可以减少编写完整迭代器的样板代码。

【讨论】:

  • 我最终让 MyIterator 继承自 boost::counting_iterator,保留对 typename ContainerType 的引用,并使用容器 operator[] 上的结果将 operator* 重定向到基类 operator* 并返回被访问的元素。
  • @nyar 保持指针不引用:不要混合值和引用语义。
【解决方案2】:

C++ 不做这样的“接口”。惯用的方法是让IContainer 的(潜在)客户端在容器类型上进行模板化,然后调用values[index],或者在迭代器类型上进行模板化,然后像*(first + offset) 一样调用。

在 C++20 中,您将能够编写 Container Concept,其行为有点像接口定义,但您已经可以将概念表达为文档化需求。

如果你想要一个类型擦除的随机访问“容器”,你可以使用boost::any_range&lt;Element, boost::random_access_traversal_tag&gt;

【讨论】:

  • 不确定“C++ 不能做”是什么意思。如果您的意思是仅模板库(例如 STL 和 Boost)不这样做,那么可以肯定的是,它们不这样做。
  • @nyarlathotep108 我的意思是标准将许多接口描述为涉及类型参数的(组)表达式的语义要求。当它进行类型擦除时,它通常不需要 user 从特定类继承。
猜你喜欢
  • 1970-01-01
  • 2012-05-09
  • 1970-01-01
  • 2012-11-07
  • 2014-08-13
  • 1970-01-01
  • 2013-02-28
  • 2011-03-03
  • 2011-05-22
相关资源
最近更新 更多