Should I prefer writing begin( c ) or c.begin() [duplicate] - c++

Every standard container has a begin and end method for returning iterators for that container. However, C++11 has apparently introduced free functions called std::begin and std::end which call the begin and end member functions. So, instead of writing
auto i = v.begin();
auto e = v.end();
you'd write
auto i = std::begin(v);
auto e = std::end(v);
In his talk, Writing Modern C++, Herb Sutter says that you should always use the free functions now when you want the begin or end iterator for a container. However, he does not go into detail as to why you would want to. Looking at the code, it saves you all of one character. So, as far as the standard containers go, the free functions seem to be completely useless. Herb Sutter indicated that there were benefits for non-standard containers, but again, he didn't go into detail.
So, the question is what exactly do the free function versions of std::begin and std::end do beyond calling their corresponding member function versions, and why would you want to use them?

How do you call .begin() and .end() on a C-array ?
Free-functions allow for more generic programming because they can be added afterwards, on a data-structure you cannot alter.

Using the begin and end free functions adds one layer of indirection. Usually that is done to allow more flexibility.
In this case I can think of a few uses.
The most obvious use is for C-arrays (not c pointers).
Another is when trying to use a standard algorithm on a non-conforming container (ie the container is missing a .begin() method). Assuming you can't just fix the container, the next best option is to overload the begin function. Herb is suggesting you always use the begin function to promote uniformity and consistency in your code. Instead of having to remember which containers support method begin and which need function begin.
As an aside, the next C++ rev should copy D's pseudo-member notation. If a.foo(b,c,d) is not defined it instead tries foo(a,b,c,d). It's just a little syntactic sugar to help us poor humans who prefer subject then verb ordering.

Consider the case when you have library that contain class:
class SpecialArray;
it has 2 methods:
int SpecialArray::arraySize();
int SpecialArray::valueAt(int);
to iterate over it's values you need to inherit from this class and define begin() and end() methods for cases when
auto i = v.begin();
auto e = v.end();
But if you always use
auto i = begin(v);
auto e = end(v);
you can do this:
template <>
SpecialArrayIterator begin(SpecialArray & arr)
{
return SpecialArrayIterator(&arr, 0);
}
template <>
SpecialArrayIterator end(SpecialArray & arr)
{
return SpecialArrayIterator(&arr, arr.arraySize());
}
where SpecialArrayIterator is something like:
class SpecialArrayIterator
{
SpecialArrayIterator(SpecialArray * p, int i)
:index(i), parray(p)
{
}
SpecialArrayIterator operator ++();
SpecialArrayIterator operator --();
SpecialArrayIterator operator ++(int);
SpecialArrayIterator operator --(int);
int operator *()
{
return parray->valueAt(index);
}
bool operator ==(SpecialArray &);
// etc
private:
SpecialArray *parray;
int index;
// etc
};
now i and e can be legally used for iteration and accessing of values of SpecialArray

To answer your question, the free functions begin() and end() by default do nothing more than call the container's member .begin() and .end() functions. From <iterator>, included automatically when you use any of the standard containers like <vector>, <list>, etc., you get:
template< class C >
auto begin( C& c ) -> decltype(c.begin());
template< class C >
auto begin( const C& c ) -> decltype(c.begin());
The second part of you question is why prefer the free functions if all they do is call the member functions anyway. That really depends on what kind of object v is in your example code. If the type of v is a standard container type, like vector<T> v; then it doesn't matter if you use the free or member functions, they do the same thing. If your object v is more generic, like in the following code:
template <class T>
void foo(T& v) {
auto i = v.begin();
auto e = v.end();
for(; i != e; i++) { /* .. do something with i .. */ }
}
Then using the member functions breaks your code for T = C arrays, C strings, enums, etc. By using the non-member functions, you advertise a more generic interface that people can easily extend. By using the free function interface:
template <class T>
void foo(T& v) {
auto i = begin(v);
auto e = end(v);
for(; i != e; i++) { /* .. do something with i .. */ }
}
The code now works with T = C arrays and C strings. Now writing a small amount of adapter code:
enum class color { RED, GREEN, BLUE };
static color colors[] = { color::RED, color::GREEN, color::BLUE };
color* begin(const color& c) { return begin(colors); }
color* end(const color& c) { return end(colors); }
We can get your code to be compatible with iterable enums too. I think Herb's main point is that using the free functions is just as easy as using the member functions, and it gives your code backward compatibility with C sequence types and forward compatibility with non-stl sequence types (and future-stl types!), with low cost to other developers.

One benefit of std::begin and std::end is that they serve as extension points
for implementing standard interface for external classes.
If you'd like to use CustomContainer class with range-based for loop or template
function which expects .begin() and .end() methods, you'd obviously have to
implement those methods.
If the class does provide those methods, that's not a problem. When it doesn't,
you'd have to modify it*.
This is not always feasible, for example when using external library, esspecially
commercial and closed source one.
In such situations, std::begin and std::end come in handy, since one can provide
iterator API without modifying the class itself, but rather overloading free functions.
Example: suppose that you'd like to implement count_if function that takes a container
instead of a pair of iterators. Such code might look like this:
template<typename ContainerType, typename PredicateType>
std::size_t count_if(const ContainerType& container, PredicateType&& predicate)
{
using std::begin;
using std::end;
return std::count_if(begin(container), end(container),
std::forward<PredicateType&&>(predicate));
}
Now, for any class you'd like to use with this custom count_if, you only have
to add two free functions, instead of modifying those classes.
Now, C++ has a mechanisim called Argument Dependent Lookup
(ADL), which makes such approach even more flexible.
In short, ADL means, that when a compiler resolves an unqualified function (i. e.
function without namespace, like begin instead of std::begin), it will also
consider functions declared in namespaces of its arguments. For example:
namesapce some_lib
{
// let's assume that CustomContainer stores elements sequentially,
// and has data() and size() methods, but not begin() and end() methods:
class CustomContainer
{
...
};
}
namespace some_lib
{
const Element* begin(const CustomContainer& c)
{
return c.data();
}
const Element* end(const CustomContainer& c)
{
return c.data() + c.size();
}
}
// somewhere else:
CustomContainer c;
std::size_t n = count_if(c, somePredicate);
In this case, it doesn't matter that qualified names are some_lib::begin and some_lib::end
- since CustomContainer is in some_lib:: too, compiler will use those overloads in count_if.
That's also the reason for having using std::begin; and using std::end; in count_if.
This allows us to use unqualified begin and end, therefore allowing for ADL and
allowing compiler to pick std::begin and std::end when no other alternatives are found.
We can eat the cookie and have the cookie - i. e. have a way to provide custom implementation
of begin/end while the compiler can fall back to standard ones.
Some notes:
For the same reason, there are other similar functions: std::rbegin/rend,
std::size and std::data.
As other answers mentions, std:: versions have overloads for naked arrays. That's useful,
but is simply a special case of what I've described above.
Using std::begin and friends is particularly good idea when writing template code,
because this makes those templates more generic. For non-template you might just
as well use methods, when applicable.
P. S. I'm aware that this post is nearly 7 years old. I came across it because I wanted to
answer a question which was marked as a duplicate and discovered that no answer here mentions ADL.

Whereas the non-member functions don't provide any benefit for the standard containers, using them enforces a more consistent and flexible style. If you at some time want to extend an existing non-std container class, you'd rather define overloads of the free functions, instead of altering the existing class's definition. So for non-std containers they are very useful and always using the free functions makes your code more flexible in that you can substitute the std container by a non-std container more easily and the underlying container type is more transparent to your code as it supports a much wider variety of container implementations.
But of course this always has to be weighted properly and over abstraction is not good either. Although using the free functions is not that much of an over-abstraction, it nevertheless breaks compatibility with C++03 code, which at this young age of C++11 might still be an issue for you.

Ultimately the benefit is in code that is generalized such that it's container agnostic. It can operate on a std::vector, an array, or a range without changes to the code itself.
Additionally, containers, even non-owned containers can be retrofitted such that they can also be used agnostically by code using non-member range based accessors.
See here for more detail.

Related

requirement on user made containers to be compliant with range-v3

I was wandering, what are the new requirement for a user defined container to be usable with Range-v3(algorithms...)?
For example, what we need to change in our design(member types, member function...) in the standard sense? How to obey Concepts on Range-v3 algorithms? what member functions do we need to provide? what are the changes for iterators?
what is the replacement for iterators/begin/end?
template<typname T>
struct container
{
//...
using value_type = T;
//...
using iterator = value_type*;
using const_iterator = const value_type*;
//...
iterator begin() { //... }
//...
};
it will be great if some can give a canonical container implementation.
what are the new ideas/notions brought by Range-v3? what is the new C++ coding way?
As far as I can tell a range itself simply needs to have begin and end members that return a valid iterator. The iterator may be a bit harder to get right. As far as I can tell, the weakest iterator (i.e. the one that needs the least members) needs the following members:
default constructor
copy constructor
operator =
operator ++ (in both pre and post forms)
operator *
operator ==
operator !=
Publicly inherit from a suitable instantiation of std::iterator so it has the right type members available -- not though that this is deprecated in C++17 so you may prefer to add the types by hand instead.
You can statically assert for the concepts and that should help you work out what you need:
static_assert(ranges::Iterator<my_iterator>(), "Not Iterator");

Use boost::circular_buffer<T> as STL container

I've written a lot of code using std::vector<T> and std::vector<T>::iterator. Now I've decided to replace the vector container with a circular buffer from boost, namely boost::circular_buffer<T>.
Of course now the compiler will complain for every function that uses std::... where I'm passing the boost::... counterpart. Do I have to rewrite all functions now? I'm asking since the container from boost works exactly the same. Also the Boost documentation states the following:
The circular_buffer is a STL compliant container. It is a kind of sequence similar to std::list or std::deque.
What does the "STL compliant" part mean? Is it referring to what I would like to do (interchangability) or is it simply a mental note for programmers, that the containers work the same way in boost as in STL?
EDIT: To give an example
class Item{ };
class Queue{
private:
std::vector<Item*> item_vector; // Want to replace only this...
std::vector<Item*>::iterator current_position; // ...and this
public:
Item* get_current_item() const {
return *current_position;
}
std::vector<Item*> get_item_vector(){
return item_vector;
}
};
Do I have to rewrite all functions now?
If your functions specifically use vector and its iterator types, then yes, you will have to change them to use different types.
If they are templates, designed to work with any sufficiently compatible container and iterator types, then they should work without change.
What does the "STL compliant" part mean?
It usually means it follows the specification for an iterable sequence defined by the C++ standard library (which was influenced by the ancient STL library, whose name some people loosely use to refer to some or all of the modern standard library).
For example, it has begin() and end() member functions returning an iterator type; and the iterator types can be incremented with ++ and dereferenced with *.
This means that an algorithm implemented as a template, for example:
template <typename InputIterator, typename T>
InputIterator find(InputIterator begin, InputIterator end, T const & value) {
for (InputIterator it = begin; it != end; ++it) {
if (*it == value) {
return it;
}
}
return end;
}
will work for any iterator type that supports these operations. While a non-generic function
void find(some_iterator begin, some_iterator end, some_type t);
will only work for a single specific iterator type, and will have to be changed or duplicated to support others.

Iterator into container which is moved

I've got a class which holds in it a container, and an iterator into that container. How can I correctly implement the move constructor? I seem to recall that by Standard, you can't rely on the iterators remaining valid after moving (which is so silly). Is there some means by which I can "update" the iterator if it was invalidated or something? Or will I have to dynamically allocate the container, move it, and then have the iterators remain valid that way?
Update: Using a std::unique_ptr as a holder for the container is the canonical generic solution - simply don't move the container, just transfer the ownership and swap the iterators. As you already said you can special-case this as an optimization, although I'd expect the generic solution to be also quite efficient and I'd only accept more complexity (aka bug-potential) to the code after proving that it's a real performance win for your use-case.
I'll leave the former answer below for future readers: Read it and the comments to see why other solutions are not really working and in which cases they cause trouble.
The obvious way to update the iterator would be:
Container c = ...;
Container::iterator it = ...;
const auto d = std::distance( c.begin(), it );
Container n = std::move(c);
it = n.begin();
std::advance( it, d );
which is generally linear, but constant when the iterator is a random access iterator.
Since you probably don't want to do that, you have two options which should help: Either default construct the new container and use swap without invalidating the iterators or put the container into a std::unique_ptr and move that instead.
The first approach (swap) requires both instances to have the container instance and this might be a bit larger than the simple, single pointer stored inside a std::unique_ptr. When you move your instances around very often, the std::unique_ptr-based approach seems preferable to me, although each access requires one more pointer indirection. Judge (and measure) for yourself what fits best in your case.
I think the implicit guarantee on iterator invalidation holds for the move ctor. That is, the following should work for all containers but std::array:
template<class Container>
struct foo_base
{
Container c;
Container::iterator i;
foo_base(foo_base&& rhs, bool is_end)
: c( std::move(rhs.c) )
, i( get_init(is_end, rhs.i) )
{}
Container::iterator get_init(bool is_end, Container::iterator ri)
{
using std::end; // enable ADL
return is_end ? end(c) : ri;
}
};
template<class Container>
struct foo : private foo_base<Container>
{
foo(foo&& rhs)
: foo_base(std::move(rhs), rhs.i == end(rhs.c))
{}
};
The complicated initialization via a base class is necessary as move assignment isn't required to move if the allocator doesn't propagate for move-assignment. The check for the iterator is required as the end() iterator may be invalidated; this check has to be performed before the container is moved. If you can ensure however that the allocator propagates (or otherwise the move-assignment doesn't invalidate iterators for your cases), you can use the simpler version below, replacing the swap with a move-assignment.
N.B. The sole purpose of the get_init function is to enable ADL. It is possible that foo_base has a member function end, which would disable ADL. The using-declaration stops unqualified lookup to find a possible member function, therefore ADL is always performed. You could as well use std::end(c) and get rid of get_init, if you're comfortable with losing ADL here.
If it should turn out that there is no such implicit guarantee for the move ctor, there's still the explicit guarantee for swap. For this, you can use:
template<class Container>
struct foo
{
Container c;
Container::iterator i;
foo(foo&& rhs)
{
using std::end; // enable ADL
bool const is_end = (rhs.i == end(rhs.c));
c.swap( rhs.c );
i = get_init(is_end, rhs.i);
}
Container::iterator get_init(bool is_end, Container::iterator ri)
{
using std::end; // enable ADL
return is_end ? end(c) : ri;
}
};
However, a swap has some requirements, defined in [container.requirements.general]/7+8:
The behavior of a call to a container's swap function is undefined unless the objects being swapped have allocators that compare equal or allocator_traits<allocator_type>::propagate_on_container_swap::value is true
[...]
Any Compare, Pred, or Hash objects belonging to a and b shall be swappable and shall be exchanged by unqualified calls to non-member swap. If allocator_traits<allocator_type>::propagate_on_container_swap::value is true, then the allocators of a and b shall also be exchanged using an unqualified call to non-member swap. Otherwise, they shall not be swapped, and the behavior is undefined unless
a.get_allocator() == b.get_allocator().
I.e. both containers should (but not have to) have equal allocators.
Move construction OTOH only requires that no exception is thrown (for allocator-aware containers); the allocator is always moved.

Why use non-member begin and end functions in C++11?

Every standard container has a begin and end method for returning iterators for that container. However, C++11 has apparently introduced free functions called std::begin and std::end which call the begin and end member functions. So, instead of writing
auto i = v.begin();
auto e = v.end();
you'd write
auto i = std::begin(v);
auto e = std::end(v);
In his talk, Writing Modern C++, Herb Sutter says that you should always use the free functions now when you want the begin or end iterator for a container. However, he does not go into detail as to why you would want to. Looking at the code, it saves you all of one character. So, as far as the standard containers go, the free functions seem to be completely useless. Herb Sutter indicated that there were benefits for non-standard containers, but again, he didn't go into detail.
So, the question is what exactly do the free function versions of std::begin and std::end do beyond calling their corresponding member function versions, and why would you want to use them?
How do you call .begin() and .end() on a C-array ?
Free-functions allow for more generic programming because they can be added afterwards, on a data-structure you cannot alter.
Using the begin and end free functions adds one layer of indirection. Usually that is done to allow more flexibility.
In this case I can think of a few uses.
The most obvious use is for C-arrays (not c pointers).
Another is when trying to use a standard algorithm on a non-conforming container (ie the container is missing a .begin() method). Assuming you can't just fix the container, the next best option is to overload the begin function. Herb is suggesting you always use the begin function to promote uniformity and consistency in your code. Instead of having to remember which containers support method begin and which need function begin.
As an aside, the next C++ rev should copy D's pseudo-member notation. If a.foo(b,c,d) is not defined it instead tries foo(a,b,c,d). It's just a little syntactic sugar to help us poor humans who prefer subject then verb ordering.
Consider the case when you have library that contain class:
class SpecialArray;
it has 2 methods:
int SpecialArray::arraySize();
int SpecialArray::valueAt(int);
to iterate over it's values you need to inherit from this class and define begin() and end() methods for cases when
auto i = v.begin();
auto e = v.end();
But if you always use
auto i = begin(v);
auto e = end(v);
you can do this:
template <>
SpecialArrayIterator begin(SpecialArray & arr)
{
return SpecialArrayIterator(&arr, 0);
}
template <>
SpecialArrayIterator end(SpecialArray & arr)
{
return SpecialArrayIterator(&arr, arr.arraySize());
}
where SpecialArrayIterator is something like:
class SpecialArrayIterator
{
SpecialArrayIterator(SpecialArray * p, int i)
:index(i), parray(p)
{
}
SpecialArrayIterator operator ++();
SpecialArrayIterator operator --();
SpecialArrayIterator operator ++(int);
SpecialArrayIterator operator --(int);
int operator *()
{
return parray->valueAt(index);
}
bool operator ==(SpecialArray &);
// etc
private:
SpecialArray *parray;
int index;
// etc
};
now i and e can be legally used for iteration and accessing of values of SpecialArray
To answer your question, the free functions begin() and end() by default do nothing more than call the container's member .begin() and .end() functions. From <iterator>, included automatically when you use any of the standard containers like <vector>, <list>, etc., you get:
template< class C >
auto begin( C& c ) -> decltype(c.begin());
template< class C >
auto begin( const C& c ) -> decltype(c.begin());
The second part of you question is why prefer the free functions if all they do is call the member functions anyway. That really depends on what kind of object v is in your example code. If the type of v is a standard container type, like vector<T> v; then it doesn't matter if you use the free or member functions, they do the same thing. If your object v is more generic, like in the following code:
template <class T>
void foo(T& v) {
auto i = v.begin();
auto e = v.end();
for(; i != e; i++) { /* .. do something with i .. */ }
}
Then using the member functions breaks your code for T = C arrays, C strings, enums, etc. By using the non-member functions, you advertise a more generic interface that people can easily extend. By using the free function interface:
template <class T>
void foo(T& v) {
auto i = begin(v);
auto e = end(v);
for(; i != e; i++) { /* .. do something with i .. */ }
}
The code now works with T = C arrays and C strings. Now writing a small amount of adapter code:
enum class color { RED, GREEN, BLUE };
static color colors[] = { color::RED, color::GREEN, color::BLUE };
color* begin(const color& c) { return begin(colors); }
color* end(const color& c) { return end(colors); }
We can get your code to be compatible with iterable enums too. I think Herb's main point is that using the free functions is just as easy as using the member functions, and it gives your code backward compatibility with C sequence types and forward compatibility with non-stl sequence types (and future-stl types!), with low cost to other developers.
One benefit of std::begin and std::end is that they serve as extension points
for implementing standard interface for external classes.
If you'd like to use CustomContainer class with range-based for loop or template
function which expects .begin() and .end() methods, you'd obviously have to
implement those methods.
If the class does provide those methods, that's not a problem. When it doesn't,
you'd have to modify it*.
This is not always feasible, for example when using external library, esspecially
commercial and closed source one.
In such situations, std::begin and std::end come in handy, since one can provide
iterator API without modifying the class itself, but rather overloading free functions.
Example: suppose that you'd like to implement count_if function that takes a container
instead of a pair of iterators. Such code might look like this:
template<typename ContainerType, typename PredicateType>
std::size_t count_if(const ContainerType& container, PredicateType&& predicate)
{
using std::begin;
using std::end;
return std::count_if(begin(container), end(container),
std::forward<PredicateType&&>(predicate));
}
Now, for any class you'd like to use with this custom count_if, you only have
to add two free functions, instead of modifying those classes.
Now, C++ has a mechanisim called Argument Dependent Lookup
(ADL), which makes such approach even more flexible.
In short, ADL means, that when a compiler resolves an unqualified function (i. e.
function without namespace, like begin instead of std::begin), it will also
consider functions declared in namespaces of its arguments. For example:
namesapce some_lib
{
// let's assume that CustomContainer stores elements sequentially,
// and has data() and size() methods, but not begin() and end() methods:
class CustomContainer
{
...
};
}
namespace some_lib
{
const Element* begin(const CustomContainer& c)
{
return c.data();
}
const Element* end(const CustomContainer& c)
{
return c.data() + c.size();
}
}
// somewhere else:
CustomContainer c;
std::size_t n = count_if(c, somePredicate);
In this case, it doesn't matter that qualified names are some_lib::begin and some_lib::end
- since CustomContainer is in some_lib:: too, compiler will use those overloads in count_if.
That's also the reason for having using std::begin; and using std::end; in count_if.
This allows us to use unqualified begin and end, therefore allowing for ADL and
allowing compiler to pick std::begin and std::end when no other alternatives are found.
We can eat the cookie and have the cookie - i. e. have a way to provide custom implementation
of begin/end while the compiler can fall back to standard ones.
Some notes:
For the same reason, there are other similar functions: std::rbegin/rend,
std::size and std::data.
As other answers mentions, std:: versions have overloads for naked arrays. That's useful,
but is simply a special case of what I've described above.
Using std::begin and friends is particularly good idea when writing template code,
because this makes those templates more generic. For non-template you might just
as well use methods, when applicable.
P. S. I'm aware that this post is nearly 7 years old. I came across it because I wanted to
answer a question which was marked as a duplicate and discovered that no answer here mentions ADL.
Whereas the non-member functions don't provide any benefit for the standard containers, using them enforces a more consistent and flexible style. If you at some time want to extend an existing non-std container class, you'd rather define overloads of the free functions, instead of altering the existing class's definition. So for non-std containers they are very useful and always using the free functions makes your code more flexible in that you can substitute the std container by a non-std container more easily and the underlying container type is more transparent to your code as it supports a much wider variety of container implementations.
But of course this always has to be weighted properly and over abstraction is not good either. Although using the free functions is not that much of an over-abstraction, it nevertheless breaks compatibility with C++03 code, which at this young age of C++11 might still be an issue for you.
Ultimately the benefit is in code that is generalized such that it's container agnostic. It can operate on a std::vector, an array, or a range without changes to the code itself.
Additionally, containers, even non-owned containers can be retrofitted such that they can also be used agnostically by code using non-member range based accessors.
See here for more detail.

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 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.