开发者

C++: overloading list.end() and list.begin() methods for constant iterators

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开发者_如何学C 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


You should provide a conversion from Iter to CIter. Standard containers do (Table 65, in 23.1 "Container requirements", says that X::iterator is convertible to X::const_iterator)

The caller can ensure that the const overload is called by using a const reference, but you shouldn't force them to do that, because they will have to write something like:

UberList<int>::CIter it = static_cast<const UberList<int> &>(ulist).begin()

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.


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

friend class UberList;
friend class CIter;

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:

UberList<T> operator = (const UberList<T>& OL)
{
    UberList<T> NL = new (OL);
    return NL;
}

Also in your main you have

it2++;
ulist.insertAfter(b, it2);
//...

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


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:

List<T> --> List<T const>

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:

Tree<Vector<T>>

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:

vector<T> const == vector<T const>

[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

teamplate<class T> bool operator!=(UberList<T>::CIter,UberList<T>::CIter);


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:

UberList<int> const & culist = ulist;
for (UberList<int>::Citer it = culist.begin(); ...)

Alternatively, use a const_cast.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜