C ++自定义迭代器和范围问题

问题描述 投票:2回答:1

假设我有一个界面。接口具有开始和结束功能,因为派生类必须实现for-range功能。用户将只使用接口,而不会知道派生类的实现。我不能对所有派生类使用相同的迭代器(更具体地说,operator ++()是不同的),因此我必须制作一个抽象的基础迭代器类。

class BaseIterator
{
    //...
public:
    virtual Type operator*()
    {
        //Implementation
    }

    virtual bool operator!=(const BaseIterator&)
    {
        //Implementation
    }

    virtual BaseIterator& operator++() = 0; 
}

//Interface
struct Interface
{
    //other pure virtual functions

    virtual BaseIterator& begin() = 0;
    virtual BaseIterator& end() = 0;
}

在继承我正在使用的接口的具体类中,可以说其中一个是类A,每个类都有其自己的迭代器,该迭代器继承自BaseIterator,并用于实现begin和end函数。

class A : public Interface
{
//...

class AIterator : public BaseIterator
    {
        AIterator& operator++()
        {
            //...
        }
    }

    public:

    AIterator& begin() 
    {
        //...
    }

    AIterator& end() 
    {
        //...
    }


}

类似地,对于其他派生类。当我尝试对多态类型使用for range循环时,会发生问题。例如(*)

Interface* c = Interface::makeA(); //assume for simplicity that there is static function in "Interface"

for(auto el : *c)
{
    //do something with el
}

由于纯虚拟函数operator ++(),出现无法实例化抽象类的错误。我认为发生这种情况的原因是在for-range循环的实现中,它等效于以下内容:

auto && __range = range_expression ;
for (auto __begin = __range.begin(), __end = __range.end(); __begin != __end; ++__begin) {
   range_declaration = *__begin;
   loop_statement;
}

我相信问题出在“ auto__begin == __range.begin()”。 begin返回对BaseIterator的引用,该引用由于自动类型推导而被删除,这最终使__begin类型成为BaseIterator,并且它是抽象类,无法实例化。我知道这种行为可以用Java实现。我在这里想念什么吗?如果没有,您将如何实现但将功能保留在(*)中?

c++ for-loop iterator polymorphism
1个回答
1
投票

[请提供完整的示例,以显示将来的问题。我想您面临的错误是类似[https://wandbox.org/permlink/PKop4WMbpuygHoes

prog.cc:73:19: error: cannot allocate an object of abstract type 'BaseIterator'
   73 |     for(auto&& i: a) {
      |                   ^
prog.cc:3:7: note:   because the following virtual functions are pure within 'BaseIterator':
    3 | class BaseIterator
      |       ^~~~~~~~~~~~
prog.cc:19:27: note:     'virtual BaseIterator& BaseIterator::operator++()'
   19 |     virtual BaseIterator& operator++() = 0;
      |                           ^~~~~~~~
prog.cc:73:19: error: cannot declare variable '__for_begin ' to be of abstract type 'BaseIterator'
   73 |     for(auto&& i: a) {
      |                   ^
prog.cc:73:19: error: cannot allocate an object of abstract type 'BaseIterator'
prog.cc:73:19: error: cannot declare variable '__for_end ' to be of abstract type 'BaseIterator'

问题很明显,因为for循环试图将begin()的返回值分配给它不能的局部变量。

C ++不是Java。基于范围的for循环可以调用begin / end成员函数,也可以调用自由函数,因此不需要接口即可获得迭代器。您的课程只需要实现这些功能,基于范围的for就可以了。除此之外,在基类中使用operator!=可以比较两个不同子类型的迭代器。要对此进行检查,您需要使用dynamic_cast来检查类型。您还需要简化实现中的参数,这显然不是一个好的设计。

继承对于C ++中的这类问题不是很突出,首选使用模板的通用代码,例如在STL中。所有算法都将与兼容的迭代器一起使用,而无需继承,因为迭代器类型是模板参数。

这里是使用简单的Range类作为容器进行迭代的示例:#include

struct Range;

struct Iterator {
    Range const* r;
    int current;

    Iterator(Range const* x, int c) : r(x), current(c) {}

    Iterator& operator++() {
        ++current;
        return *this;
    }

    int const& operator*() const {
        return current;
    }
};

bool operator==(Iterator const& x, Iterator const& y) {
    return x.current == y.current;
}

bool operator!=(Iterator const& x, Iterator const& y) {
    return !(x == y);
}

struct Range {
    int min;
    int max;

    Iterator begin() const {
        return Iterator(this, min);
    }

    Iterator end() const {
        return Iterator(this, max+1);
    }
};


int main() {
    Range r{-5, 5};
    for(auto&& i: r) {
        std::cout << i << std::endl;
    }
}

这是一个简化且不完整的示例,仅用于说明如何使其与基于范围的for循环一起使用。如果要实现与标准库中的迭代器一致的迭代器,则需要添加一堆成员函数和typedef。应该有很多有关如何编写这些迭代器的信息。

PS:如果决定对接口使用类似Java的方法,请不要忘记为接口提供(公共或受保护的)虚拟析构函数,并将副本构造函数声明为delete以防止对象切片。

© www.soinside.com 2019 - 2024. All rights reserved.