【问题标题】:Design a base iterator for a base class为基类设计基迭代器
【发布时间】:2014-04-16 12:57:19
【问题描述】:

我目前正在设计一个接口(在以下示例中为Base),它提供了几个虚拟方法,包括begin()end()。这两个方法简单地返回相应的迭代器,就像在任何其他集合(如类)中一样。派生类应实现这些方法并返回其特定的迭代器实现。

下面的(简化的)示例显示了一个派生类,它使用boost::transform_iterator 来转换私有内部整数列表。这个实现只是现实中的一个例子,迭代的“东西”可以是别的东西,迭代器也是如此。

该示例有效,但我有一个问题。 main() 中的对象类型并没有隐藏使用的迭代器是 TransformIterator 类型的事实。基类将用于某种插件架构,其中每个插件都是一个共享库。插件不应该知道使用了哪种类型的迭代器,而应该完全依赖于抽象接口。有没有办法做到这一点?

#include <boost/iterator/transform_iterator.hpp>

#include <iostream>
#include <string>
#include <vector>

template<class Iterator>
class Base
{
public:
    virtual Iterator begin() = 0;
    virtual Iterator end() = 0;
};

struct toString
{
    std::string operator()(int number) const
    {
        return std::to_string(number);
    }
};

typedef boost::transform_iterator<toString, std::vector<int>::iterator> TransformIterator;

class Derived : public Base<TransformIterator>
{
public:
    Derived() : ints{132, 3, 6451, 12, 5} {}

    TransformIterator begin()
    {
        return boost::make_transform_iterator(ints.begin(), toString());
    }

    TransformIterator end()
    {
        return boost::make_transform_iterator(ints.end(), toString());
    }

private:
    std::vector<int> ints;
};

int main()
{
    Base<TransformIterator>* obj = new Derived();
    for(const auto& value : *obj)
    {
        std::cout << value.size() << std::endl;
    }
    return 0;
}

更多背景知识:这个具体示例基于读取配置文件的接口。目前我只打算为 YAML 文件提供一个实现,但其他格式,如 XML 或老式 INI 也是可能的。因此是一个通用接口。

【问题讨论】:

    标签: c++ c++11 iterator template-classes


    【解决方案1】:

    不是经典的 C++ 迭代器,不是。它们不适合多态使用。

    您可以做的是为您的迭代器定义一个抽象基类,然后由每个具体迭代器类型的(模板化)包装器实现。抽象基类简单地将所有必需的运算符定义为纯虚拟。缺点是迭代器上的每个操作都需要一个虚函数调用...根据您的使用情况,这可能会或可能不会成为问题(通常不会,除非您使用它们频繁迭代非常大的集合)。

    例子:

    template <typename T>
    class InputIterator<T>
    {
      public:
        virtual T operator*() const = 0;
    
        // Other operators go here
    };
    
    template <typename TIterator>
    class ConcreteInputIterator final
    :  public InputIterator<typename std::iterator_traits<TIterator>::value_type>
    {
      public:
        ConcreteInputIterator(TIterator iterator) : m_iterator(iterator) {}
    
      public:
        virtual T operator*() const override
        {
          return *m_iterator;
        };
    
      private:
        TIterator m_iterator;
    };
    

    【讨论】:

    • 啊,现在我明白为什么没有找到解决方案了。在这种特定情况下,虚函数调用应该没有任何问题。配置在启动时读取一次。
    • 注意:要与 STL 算法一起使用,您需要为抽象迭代器专门化 std::iterator_traits,或者从已经具有适当专门化的 std::iterator 派生它们。跨度>
    【解决方案2】:

    我最近在工作中的一个项目中做了非常相似的事情。我这样做的方法是引入一个抽象迭代器接口和一个用于访问该接口的迭代器的类。这是一个简化的版本:

    template <class ValueType>
    struct AbstractIterator
    {
      virtual ValueType& dereference() const = 0;
      virtual void increment() = 0;
      // and so on...
    };
    
    
    template <class ValueType>
    struct ClientIterator : public std::iterator<whatever>
    {
      std::unique_ptr<AbstractIterator<ValueType>> it;
    
      ClientIterator(std::unique_ptr<AbstractIterator<ValueType>> it) : it(std::move(it)) {}
    
      ValueType& operator* const () { return it->dereference(); }
      ClientIterator& operator++ () { it>increment(); return *this; }
    
      // and so on...
    };
    
    struct Base
    {
      typedef ClientIterator<std::string> Iterator;
      virtual Iterator begin() = 0;
      virtual Iterator end() = 0;
    }
    
    
    struct Derived : Base
    {
      struct ServerIterator : AbstractIterator<std::string>
      {
        boost::transform_iterator<toString, std::vector<int>::iterator> it;
    
        virtual std::string& dereference() const override { return *it; }
        virtual void increment() override { ++it; }
      }
    
      virtual Iterator begin() override { return Iterator({new ServerIterator(something)}); }
      virtual Iterator end() override { return Iterator({new ServerIterator(anotherThing)}); }
    };
    

    【讨论】:

    • 谢谢你,我稍后会检查你的例子。
    • virtual + override 好像有点啰嗦,为什么不只override
    • @TemplateRex 没有理由,只是习惯将virtual 放在覆盖器上(从没有override 的时候起)。
    • 我喜欢这个实现,因为它仍然允许您像往常一样按值使用迭代器,即Derived d; for (auto it = d.begin(); it != d.end(); ++it) { foo(*it); }。一个更简单的实现可以只返回一个指向AbstractIterator 的指针,而不将其包装在ClientIterator 中,但是你需要编写类似Derived d; for (AbstractIterator *it = d.begin(); *it != d.end(); ++*it) { foo(**it); } 的东西。没有那么多额外的代码,但是容易出错……
    • 不应该用移动语义定义构造函数吗? IE。 ClientIterator(std::unique_ptr&lt;AbstractIterator&lt;ValueType&gt;&gt; &amp;&amp;it)
    猜你喜欢
    • 2014-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-05
    • 1970-01-01
    • 1970-01-01
    • 2010-11-30
    • 1970-01-01
    相关资源
    最近更新 更多