【问题标题】:Iterator Inheritance and inheriting *this迭代器继承和继承 *this
【发布时间】:2016-09-20 21:32:39
【问题描述】:

如何编写一个基类和几个派生类的迭代器?

迭代器是否必须返回自身(*this)?

到目前为止,我使用typename Xstatic_cast<X&>(*this) 来允许派生类继承从基类返回自身的函数。

This 看起来很丑。有没有更好的办法?

简化代码:

#include <iterator>
#include <iostream>
template <typename T, typename X>
class BaseIterator : public std::iterator<std::input_iterator_tag, T> {
    //Not intended to be used directly.
    private:
        T* p;
    protected:
        virtual void increment(void)=0;
        virtual T* stride_index(int index)=0;
    public:
        virtual ~BaseIterator(){} //virtual destructor.
        X operator++(int) { //takes a dummy int argument
            X tmp(static_cast<X&>(*this) );
            increment();
            return tmp;
        }
        bool operator==(const X & rhs) { return p==rhs.p; }
} ;
template <typename T>
class ContiguousIterator : public BaseIterator<T, ContiguousIterator<T> > {
    private:
        T* p;
    protected:
        inline void increment(void) {++p;}
        inline T* stride_index(int index){return p + index;}
    public:
        virtual ~ContiguousIterator(){} //destructor.
        ContiguousIterator(T* x) :p(x) {}
        ContiguousIterator(const ContiguousIterator<T> & mit) : p(mit.p) {}
} ;

int main(void){
    int i[]={0,1,2,3,4,5};
    ContiguousIterator<int> itbegin(i);
    ContiguousIterator<int> it(i);
    it++;
    std::cout << "result: " << (it == itbegin) << std::endl;
}

另一种方法是忘记使用继承来编写更少的代码。只需将返回 *this 的函数复制并粘贴到派生类即可。

我似乎越来越能接受这种选择......

【问题讨论】:

  • 另外,ContiguousIterator 有两个不相关的公共 T* p 成员。那只是混乱。
  • 我不知道crtp。 crtp 在这种情况下会有帮助吗?
  • 简单修复:将 T* p 更改为私有。无法将其更改为受保护。不知何故。我以前试过。
  • 查看 25 次后没有解决方案。为什么?迭代器的继承只是一个坏主意吗?
  • 因为不清楚你为什么要做你正在做的事情,这是很多代码解释不佳,而“显而易见”的解决方案需要我进行大量测试和编写对的。但是:完成。

标签: c++ inheritance iterator this crtp


【解决方案1】:

一般来说,virtual 对于应该是轻量级的迭代器之类的东西来说是很多开销。通常的方法是 CRTP。这有点棘手,但看起来像这样:

template <typename T, typename X>
class BaseIterator : public std::iterator<std::input_iterator_tag, T> {
    protected:
        T* p;
        X* self() {return static_cast<X*>(this);}
        const X* self() const {return static_cast<const X*>(this);}
        BaseIterator(T* x) :p(x) {}
    public:
        X operator++(int) { //takes a dummy int argument
            X tmp(*self());
            self()->increment();
            return tmp;
        }
        bool operator==(const X & rhs) const { return p==rhs.p; }
} ;

基类通常采用派生类型,加上函数签名所需的任何东西作为模板参数。然后添加两个self() 函数,它们为您提供派生类型,这意味着您实际上不需要virtual。一切都被编译器简单地内联。 (注意我也给了它一个理智的构造函数,并制作了operator==const

template <typename T>
class ContiguousIterator : public BaseIterator<T, ContiguousIterator<T> > {
    public:
        typedef BaseIterator<T, ContiguousIterator<T> > parent;
        void increment(void) {++(this->p);}
        T* stride_index(int index) const {return this->p + index;}
    public:
        virtual ~ContiguousIterator(){} //destructor.
        ContiguousIterator(T* x) :parent(x) {}
        ContiguousIterator(const ContiguousIterator<T> & mit) : parent(mit.p) {}
} ;

然后派生类型简单地继承正常,除了你必须使用this-&gt; 来访问成员,因为父是一个模板。在这里看到它的工作:http://coliru.stacked-crooked.com/a/81182d994c7edea7

这产生了一个高效的迭代器,没有开销。我相信这种技术在 Boost 的迭代器库中被大量使用:http://www.boost.org/doc/libs/1_59_0/libs/iterator/doc/#new-style-iterators

【讨论】:

  • 天哪。我不知道 virtual 会给迭代器带来巨大的开销。 programmers.stackexchange.com/questions/191637/… 非常感谢您的回答。
  • @rxu:鉴于您不使用基础,虚拟可能不会为您的案例增加任何开销。但这保证不会。
  • 为基类声明一个虚拟析构函数是个好主意吗?这会产生任何开销吗?
  • 嗯。基类的虚析构函数也会被调用:stackoverflow.com/questions/2182925/virtual-destructor 但是如果基类的虚析构函数什么都不做。可以吗?
  • @rxu:声明虚拟析构函数确实会增加内存开销,如果您的代码有BaseIterator&lt;T,ContiguousIterator&lt;T&gt;&gt; 指针或引用,那么代码可能会有一些时间开销。但我无法想象你为什么会有一个。没有它,虚拟析构函数就没有任何好处。
猜你喜欢
  • 2011-01-26
  • 1970-01-01
  • 1970-01-01
  • 2021-12-08
  • 1970-01-01
  • 1970-01-01
  • 2019-04-26
  • 1970-01-01
  • 2012-01-27
相关资源
最近更新 更多