我有一个虚拟容器“ 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;
然后我通过MyIterator在Array中继承它。
通过我尝试的操作,我在运行简单的forrange循环时收到“无效的二进制表达式操作数('ISequence :: IIterator'和'ISequence :: IIterator')”错误消息。
答案 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