C++:为常量迭代器重载 list.end() 和 list.begin() 方法
我仍在尝试实现我自己的 LinkedList 类版本,现在我在常量迭代器的重载方法方面遇到问题。例如,当我尝试使用以下代码打印列表时:
cout << "citer:" << endl;
for (UberList<int>::CIter it = ulist.begin(); it != ulist.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
我有这些错误:
Error E2034 UberList2.cpp 532: Cannot convert 'UberList<int>::Iter' to 'UberList<int>::CIter' in function main()
Error E2094 UberList2.cpp 532: 'operator!=' not implemented in type 'UberList<int>::CIter' for arguments of type 'UberList<int>::Iter' in function main()
据我所知,这意味着使用了那些通常的结束和开始迭代器方法。以下是这些方法在我的类中的声明方式:
Iter begin();
Iter end();
CIter begin() const;
CIter end() const;
有
template<class T>
typename UberList<T>::Iter UberList<T>::begin()
{
Iter it;
it.curr = head;
return it;
}
template<class T>
typename UberList<T>::Iter UberList<T>::end()
{
Iter it;
it.curr = tail->next;
return it;
}
template<class T>
typename UberList<T>::CIter UberList<T>::begin() const
{
CIter it;
it.ccurr = head;
return it;
}
template<class T>
typename UberList<T>::CIter UberList<T>::end() const
{
CIter it;
it.ccurr = tail->next;
return it;
}
什么方法可以强制我的程序对常量迭代器使用这些 const 方法而不是通常的方法?我很高兴听到任何建议。
哦,这是我的班级代码在一个文件中,以防万一: http://pastebin.com/Jbvv5Hht
I'm still trying to implement my own version of LinkedList class and now I have problems with overloading methods for constant iterators. For example, when I try to print out list using this code:
cout << "citer:" << endl;
for (UberList<int>::CIter it = ulist.begin(); it != ulist.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
I have these errors:
Error E2034 UberList2.cpp 532: Cannot convert 'UberList<int>::Iter' to 'UberList<int>::CIter' in function main()
Error E2094 UberList2.cpp 532: 'operator!=' not implemented in type 'UberList<int>::CIter' for arguments of type 'UberList<int>::Iter' in function main()
so as far as I understood, it means that those usual end and begin iterator methods are used. Here's how these methods are declared in my class:
Iter begin();
Iter end();
CIter begin() const;
CIter end() const;
and
template<class T>
typename UberList<T>::Iter UberList<T>::begin()
{
Iter it;
it.curr = head;
return it;
}
template<class T>
typename UberList<T>::Iter UberList<T>::end()
{
Iter it;
it.curr = tail->next;
return it;
}
template<class T>
typename UberList<T>::CIter UberList<T>::begin() const
{
CIter it;
it.ccurr = head;
return it;
}
template<class T>
typename UberList<T>::CIter UberList<T>::end() const
{
CIter it;
it.ccurr = tail->next;
return it;
}
Is there any way I can force my program to use these const methods for constant iterators instead of usual ones? I'd be glad to hear any advice.
Oh and here's the code of my class in one file just in case: http://pastebin.com/Jbvv5Hht
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您应该提供从
Iter
到CIter
的转换。标准容器确实如此(表 65,在 23.1“容器要求”中,表示X::iterator
可转换为X::const_iterator
)调用者可以确保
>const
重载是通过使用const
引用来调用的,但您不应该强迫他们这样做,因为他们必须编写如下内容:如果您提供“必需”转换那么你的调用者不需要做任何特殊的事情:你的原始代码将工作,就像标准容器一样。
You should provide a conversion from
Iter
toCIter
. Standard containers do (Table 65, in 23.1 "Container requirements", says thatX::iterator
is convertible toX::const_iterator
)The caller can ensure that the
const
overload is called by using aconst
reference, but you shouldn't force them to do that, because they will have to write something like:If you provide the "required" conversion then there's no need for your caller to do anything special: your original code will work, just as it does for standard containers.
对OP代码的更多评论。考虑分离巨大的 uberl33tlist 类并将其分解为更小的文件。看到所有这些朋友的类声明让我很不舒服。当您使用类似“
在某些情况下”之类的内容时,还有一些棘手的语义,这些语句最终也会转发声明这些类(如果它们尚不存在)。这里的赋值运算符也有一些不太正确的地方:
另外,在你的 main 中,你
为 it2 使用了后缀运算符 ++,但没有实现你的迭代器类。 Borland 通过使用前缀来接受它,但会给出警告。 Gcc 实际上将其标记为错误并拒绝该代码。可能想调查一下
Some more comments to the OP's code. Consider separating that gigantic uberl33tlist class and break it up into smaller files. Seeing all those friends class declaration is making me rather uncomfortable. There are also some tricky semantics When you're using stuff like
In some cases those statements also end up forward declaring those classes if they don't exist yet. There's also something not quite right looking about your assignment operator here:
Also in your main you have
You're using postfix operator ++ for it2 but didn't implement that your iterator class. Borland accepts it by using the prefix instead but gives a warning. Gcc actually flags that as an error and rejects the code. Might want to look into that
叹息:这里有一定程度的黑客行为,旨在隐藏这样一个事实:从概念上讲,您想要做的事情无法在 C++ 中自动完成,因为它不理解方差。其他一些语言(包括 Ocaml)也可以。
如果您有一个仿函数(对于 C++ 程序员来说是一个模板类),问题是它和各种函数如何在参数变化的情况下表现,例如从 T 到 T const 的转换。您真正想要的是:
换句话说,您希望 List 函子是协变的。但不,它不是..所以实际上 List 模板根本不是函子,因为函子必须保留结构并且转换不会按要求反映。反过来,这意味着要么 C++ 模板被破坏,要么 const 的概念被破坏,因为不支持参数多态性的类型系统被规范破坏了:)
提供“const_iterator”并不能解决这个问题,它只是修补中断。易失性和常量易失性版本在哪里?双重间接怎么样?
如果您不理解双重间接寻址:请考虑 T 的向量树,这是两个模板:
这里最好的解决方案是放弃支持 const_iterator。只是别打扰。无论如何,它很困惑:“const向量”怎么样?那是什么?一个你不能做得更长但仍然允许你写元素的向量?
实际的要求是变换通勤,例如:
[或者如果变换是逆变的,则它们是反通勤的]
事实上,这种情况没有发生,表明向量不是函数式的,换句话说,不能使用模板有效地进行参数多态性。如果您真的想把内裤打结,请考虑带有函数参数的模板,并询问函数返回类型和参数的差异,以及这可能如何影响容器。一个很好的例子是如何组合两个函数,以便它们成对运行。如果它们是变异者怎么办,那么“const”如何工作?
Sigh: there's a level of hackery here designed to hide the fact that conceptually what you want to do cannot be done automatically in C++ because it doesn't understand variance. Some other languages (including Ocaml) do.
If you have a functor (that's a template class to C++ programmers), the question is how it and various functions behave with a variance of the parameter, such as a conversion from T to T const. What you really want is this:
in other words you want the List functor to be covariant. But no, it isn't .. so actually the List template isn't a functor at all, because functors must be structure preserving and the conversion isn't reflected as required. In turn this means either C++ templates are broken OR the concept of const is broken, because a type system that doesn't support parametric polymorhism is broken by specification :)
Providing "const_iterator" does not solve this problem, it simply patches up the break. Where is the volatile and const_volatile version? How about double indirections?
If you don't understand double indirections: consider a tree of vectors of T, that's two templates:
The best solution here is to give up supporting const_iterator. Just don't bother. It's confused anyhow: how about "const vector"? What's that? A vector you can't make longer but it still allows you to write the elements?
The actual requirement is that transforms commute, for example:
[or they anti-commute if the transform is contra-variant]
The fact this doesn't happen shows that vector isn't functorial, in other words, templates can't be used effectively for parametric polymorphism. If you want to really get your knickers tied in a knot consider templates with function arguments and ask about the variance of the function's return type and parameters, and how this might impact the container. A good example is how to compose a two functions so they work on a pair. What if they're mutators, how does "const" work then?
你需要
You need
它使用常规的 begin 方法,因为变量不是 const。因此,修复它的一种方法是创建另一个 const(引用)变量:
或者,使用 const_cast。
It's using the regular begin method because the variable is not const. So one way of fixing it is to make another (reference) variable that is const:
Alternatively, use a const_cast.