【问题标题】:Is it possible to create a virtual iterator class?是否可以创建一个虚拟迭代器类?
【发布时间】:2019-10-09 12:59:16
【问题描述】:

我有一个虚拟容器“ISequence”,它用作数组或列表上容器实现的模板。我已经为 Array 和 List 容器实现了一个迭代器。我想做的是创建一个虚拟 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;

然后我通过 MyIterator in Array 继承它。

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

【问题讨论】:

  • 您没有提供确切的上下文,但我怀疑这是由于缺少 IIterator 的比较运算符。附带说明一下,您可能需要重新考虑将 const_iterator 定义为 const 类型。
  • 标准库 (STL) 使其与众不同。 标头有一个通用算法,如for_eachremove_if 等,适用于任何迭代器。
  • 回答标题问题,是的。这正是std::iterator 所做的。
  • @sweenish std::iterator 不是虚拟的
  • 你为什么要这样做?我根本不相信任何这些都需要做任何你想做的事情。关于这一点:请分享您真正想要实现的目标。显示您想到的将要使用它的代码。可以肯定的是,该代码可以在 C++ 中运行,而无需执行您建议的任何操作,并且它与 C#(或 Python)读起来并没有什么不同。

标签: c++ iterator virtual


【解决方案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 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-15
    • 2017-12-06
    • 2022-01-10
    • 2011-03-27
    • 2022-01-15
    • 2019-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多