是否可以创建虚拟迭代器类?

时间:2019-10-09 12:59:16

标签: c++ iterator virtual

我有一个虚拟容器“ ISequence”,它用作数组或列表上的容器实现的模板。我已经为数组和列表容器实现了一个迭代器。我想要做的是创建一个虚拟IIterator类,以便我可以创建使用迭代器并接受ISequence的算法作为参数。我的目标是实现自由。

我尝试不使用任何方法将虚拟IIterator类添加到ISequence中,并且该类继承了std :: iterator,但不起作用

ISequence类:

template <typename T>
class ISequence {
protected:
    int length; //length of sequence
public:
    virtual int getLength() const = 0; //get length of sequence
    virtual bool getIsEmpty() const = 0; //check if empty
public:
    virtual T get(int index) const = 0; //get item based on index
    virtual T getFirst() const = 0; //get first item
    virtual T getLast() const = 0; //get last item
    virtual ISequence<T>* getSubSequence(int startIndex, int endIndex) const = 0;
    virtual void append(T item) = 0; //add item to the end
    virtual void prepend(T item) = 0; //add item to the beginning
    virtual void insertAt(int index, T item) = 0; //insert item at a specific point
    virtual void remove(T item) = 0; //remove specific item
    virtual void replace(int index, T item) = 0; //replace an item
};

带有迭代器的数组:

template <typename T>
class ArraySequence: public ISequence<T> {
private:
    T* data;
public:
    ArraySequence();
    ArraySequence(ISequence<T>* sequence);
    ArraySequence(int n, int leftLimit, int rightLimit);
    ArraySequence<T>& operator=(const ArraySequence<T>& sequence);
    ~ArraySequence();
public:
    virtual int getLength() const override;
    virtual bool getIsEmpty() const override;
public:
    virtual T get(int index) const override;
    virtual T getFirst() const override;
    virtual T getLast() const override;
    virtual ArraySequence<T>* getSubSequence(int startIndex, int endIndex) const override;
    virtual void append(T item) override;
    virtual void prepend(T item) override;
    virtual void insertAt(int index, T item) override;
    virtual void remove(T item) override;
    virtual void replace(int index, T item) override;
private:
    class MyIterator: public std::iterator<std::random_access_iterator_tag, T> {
        friend class ArraySequence;
    private:
        T* pos;
        MyIterator(T* pos);
    public:
        MyIterator(const MyIterator &it);
        ~MyIterator();
    public:
        typename MyIterator::reference operator*() const;
        typename MyIterator::pointer operator->() const;
        typename MyIterator::reference operator[](const typename MyIterator::difference_type& n) const;
        typename MyIterator::difference_type operator-(const MyIterator& it) const;
        MyIterator operator++(int);
        MyIterator& operator++();
        MyIterator operator--(int);
        MyIterator& operator--();
        MyIterator operator+(const typename MyIterator::difference_type& n) const;
        MyIterator& operator+=(const typename MyIterator::difference_type& n);
        MyIterator operator-(const typename MyIterator::difference_type& n) const;
        MyIterator& operator-=(const typename MyIterator::difference_type& n);
        bool operator!=(const MyIterator& it) const;
        bool operator==(const MyIterator& it) const;
        bool operator<(const MyIterator& it) const;
        bool operator>(const MyIterator& it) const;
        bool operator<=(const MyIterator& it) const;
        bool operator>=(const MyIterator& it) const;
    };
public:
    typedef MyIterator iterator;
    typedef MyIterator const const_iterator;
    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;
};

我的抽象IIterator代码(作为ISequence的补充):

protected:
    class IIterator: public std::iterator<std::random_access_iterator_tag, T> { //virtual Iterator class
    };
public:
    typedef IIterator iterator;
    typedef IIterator const const_iterator;
    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;

然后我通过My​​Iterator在Array中继承它。

通过我尝试的操作,我在运行简单的forrange循环时收到“无效的二进制表达式操作数('ISequence :: IIterator'和'ISequence :: IIterator')”错误消息。

1 个答案:

答案 0 :(得分:1)

您要实现的概念在C#中非常基础。也许有充分的理由在C ++中不存在任何理由?是的!

您正在尝试使用C ++实现.Net库。 这不是一个好主意,因为该库的设计迎合了C#和CLR(或近来他们所说的任何东西)的限制。 C#在编译时对类型的处理要少得多,并且它没有像C ++那样的编译时通用类型替换系统。通过不使用C ++所提供的功能,您可以重新发明轮子并编写看起来很普通的代码。而且它可能会比需要的慢,因为迭代器上的虚拟方法调用易于被频繁调用,其开销显示出来。编译器将在可能的情况下取消某些调用的虚拟化,但这不是要依赖的内容。

概括一点; 除非另有证明,否则默认情况下直接使用C ++中的.Net生态系统中的大多数惯用法都是错误的。

在C ++中,“接口”的概念并不取决于虚拟方法,而是取决于概念-即对类型的约束。在C#中无法实现此目的,在CLR中也无法实现,因此设计不支持它。在C ++中,只要所使用的具体类型符合调用者要求的约束(例如,可迭代),编译器就会处理其余的工作。习惯上,您可以使用ranges或迭代器来传递容器,并且接受它们的所有代码都应该是通用的:容器或迭代器的类型应该是模板参数,并且不应强制将迭代器设置为类型相同-这确实是旧版C ++库中的库设计错误。

因此,C ++的优点在于您不必定义任何接口。在C ++ 20中,您可以使用概念来约束类型,但是除此之外,它很简单-比C#中的要简单得多。范围的想法是使对象具有“类似”容器的作用,即可以使用range-for对其进行迭代,但它们不一定是容器-它们可能只是在容器上表达一系列元素的一种方式,甚至是动态生成。

如果您正在考虑执行此操作以支持将代码从C#移植到C ++,则只需忽略对ISequence等的需求。只需使用通用类型参数,如果需要,可以选择使用概念限制它们使用C ++ 20编译器,就可以开始工作了。就这么简单。容器可以是任何东西。甚至是简单的“ C”数组(颤抖-不要使用它们,请始终使用std::array来代替!)。

假设我们有以下C#代码:

System.IO.TextWriter cout = Console.Out;

Action<System.Collections.IEnumerable> printValues = (values) =>
{
    cout.WriteLine("printValues");
    foreach (var v in values)
        cout.Write($"{v} ");
    cout.Write("\n");
};

var list_of_ints = new List<int>{1, 2, 3, 4, 5};
var vector_of_strings = new String[]{"a", "b", "c", "d"};
cout.WriteLine("* Entire containers");
printValues(list_of_ints);
printValues(vector_of_strings);
cout.WriteLine("\n* Subranges of containers");
printValues(list_of_ints.GetRange(1, list_of_ints.Count() - 1));
printValues(vector_of_strings.Take(vector_of_strings.Count() - 1));

输出:

* Entire containers
printValues
1 2 3 4 5
printValues
a b c d

* Subranges of containers
printValues
2 3 4 5
printValues
a b c

只要您坚持使用惯用的C ++并且不要尝试在C ++中重新实现.Net,它在C ++中看起来不会有太大不同。

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

// This will accept entire containers, as well as C++20 ranges.
template <class C> void printValues(const C &values) {
    std::cout << __FUNCTION__ << " with container\n";
    for (auto &v : values)
        std::cout << v << " ";
    std::cout << "\n";
}

// This is the more legacy way of doing it - to stay compatible with C++98 (shiver).
template <class I1, class I2> void printValues(I1 start, I2 end) {
    std::cout << __FUNCTION__ << " with iterators\n";
    for (; start != end; ++start)
        std::cout << *start << " ";
    std::cout << "\n";
}

int main() {
    std::forward_list<int> list_of_ints{1,2,3,4,5};
    std::vector<std::string> vector_of_strings{"a", "b", "c", "d"};

    std::cout << "* Entire containers\n";
    printValues(list_of_ints);
    printValues(vector_of_strings);
    std::cout << "\n* Subranges of containers\n";
    printValues(std::next(list_of_ints.cbegin()), list_of_ints.cend());
    printValues(vector_of_strings.cbegin(), std::prev(vector_of_strings.cend()));
}

输出:

* Entire containers
printValues with container
1 2 3 4 5 
printValues with container
a b c d 

* Subranges of containers
printValues with iterators
2 3 4 5 
printValues with iterators
a b c