我能否轻松地覆盖(STL)迭代器的类别?

4

现在,我有一个可以通过随机访问迭代器满足API要求的类。但是,我可以想象到这种实现会发生变化,只能提供前向迭代器。

因此,我想限制调用者使用随机访问功能。我知道我可以编写自己的实现(例如restricted_bar_iterator),但想知道是否有更简单的方法(即需要更少的编码)。

class BAR { ... };

class FOO {
public:
    // Bad...clients may expect 'bar_iterator' to be random access...
    typedef std::vector<BAR>::iterator bar_iterator;

    bar_iterator begin_bar() const;
    bar_iterator end_bar() const;

    // Possible solution here!
    class restricted_bar_iterator :
        public std::iterator< std::forward_iterator_tag, BAR > { ... };
};


void baz()
{
    FOO foo;
    bar_iterator it = foo.begin_bar() + 5; // want a compile time error here!
}

我会尽最大努力避免这种情况,但我真的想这样做,我会选择vector::[const_]iterator的公共继承和std::iterator_traits的显式特化,因为这需要的代码最少。虽然这是可行的,但最好完全避免这种情况。 - Alexandre C.
继承自 std::iterator<std::forward_iterator_tag, T> 并定义所有你需要的方法,这样它的行为就会和你期望的完全一致。 - andre
@AlexandreC.,即使vector<BAR>::iterator是一个类类型,你仍然需要至少替换两个operator++重载,否则它们将返回基础类型,并且仅特化iterator_traits并不能阻止上面的代码尝试使用RandomAccessIterator操作,即使该类别为ForwardIterator。 - Jonathan Wakely
@JonathanWakely:你可以在派生类中隐藏成员(包括operator++),或者你也可以使用私有继承和using指令。我发现这比手动继承std::iterator并转发函数要清晰得多。关于vector::iterator是否具有类类型的好点,但标准中可能有一些暗示。 - Alexandre C.
2
@AlexandreC:标准确实允许std::vector<T>::iteratorT* - Dietmar Kühl
显示剩余3条评论
2个回答

4
这里有一个使用Boost迭代器适配器的示例。我使用了int而不是BAR。请参考Boost Iterator Adaptor
#include <boost/iterator/iterator_adaptor.hpp>
#include <vector>

struct iterator :
    public boost::iterator_adaptor<
        iterator,                    // the name of our class, see docs for details
        std::vector<int>::iterator,  // underlying base iterator
        boost::use_default,          // for value type
        boost::forward_traversal_tag // all the boilerplate for this!
    >
{
     // need this to convert from vector::iterator to ours
     explicit iterator(std::vector<int>::iterator i)
      : iterator::iterator_adaptor_(i) {}
};

int main()
{
    std::vector<int> v;
    iterator it(v.begin());
    ++it;    // OK
    it += 1; // ERROR
}

这实际上将std::vector<T>::iterator用作基类,但仅允许定义为前向迭代器的操作。缺点是错误消息不太友好。


3

您肯定需要进行一些编码,但是您可能可以继承底层类型以获得大部分功能,只需覆盖不希望工作的操作,无论是在C++11中定义为已删除还是在C++03中将其设置为私有和未实现:

class FOO {
    // Bad...clients may expect 'bar_iterator' to be random access...
    typedef std::vector<BAR>::iterator bar_iterator_impl;

public:
    // Possible solution here!
    struct bar_iterator : bar_iterator_impl {
      bar_iterator& operator++() {
        ++static_cast<bar_iterator_impl&>(*this);
        return *this;
      }
      bar_iterator operator++(int) {
        bar_iterator copy(*this);
        ++*this;
        return copy;
      }

      typedef std::forward_iterator_tag iterator_category;    
      typedef std::iterator_traits<bar_iterator_impl>::value_type value_type;
      typedef std::iterator_traits<bar_iterator_impl>::difference_type difference_type;
      typedef std::iterator_traits<bar_iterator_impl>::pointer    pointer;
      typedef std::iterator_traits<bar_iterator_impl>::reference  reference;

    private:
      friend void operator+(bar_iterator const&, long);
      friend void operator+(long, bar_iterator const&);
      friend void operator-(bar_iterator const&, long);
      friend void operator-(long, bar_iterator const&);
    };

    bar_iterator begin_bar() const;
    bar_iterator end_bar() const;
};

然而,这仅适用于 std::vector<BAR>::iterator 是一个类类型,如果它是一个指针类型,那么就无法派生它。为了实现可移植性,您需要自己定义整个迭代器 API。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接