-
September 24th, 2014, 05:16 AM
#1
Overloading std::distance for a specific iterator type
I have a templated container that defines a forward iterator.
Calling std:istance on these iterators will generate code that will count the number of iterations it takes to get from the first parameter to the second, by repetitively incrementing.
Internally, the iterators can easily find the distance by a simple subtraction.
What I want to do is overload std:istance for these iterators so that it will take advantage of the simple calculation rather than repetitive increments.
The simple solution of course would be to make the iterators random access, but this would require that they support functionality that is not 'logical' for the container. Access to the container only makes logical sense when iterating one item at a time in the forward direction.
I haven't as yet discovered the correct syntax for this (and I'm beginning to think that it may not be possible).
Code:
#include <iterator>
template <typename T>
class Container
{
public:
class iterator : public std::iterator<std::forward_iterator_tag, T>
{
public:
friend long operator -(iterator lhs, iterator rhs)
{
return lhs.index - rhs.index;
}
private:
size_t index;
};
};
namespace std
{
// This does not compile.
template <typename T>
long distance<T>(const typename Container<T>::iterator& begin,
const typename Container<T>::iterator& end)
{
return end - begin;
}
}
int main()
{
Container<int>::iterator ib;
Container<int>::iterator ie;
long d = std::distance(ib, ie); // Should call the specialised version.
return 0;
}
"It doesn't matter how beautiful your theory is, it doesn't matter how smart you are. If it doesn't agree with experiment, it's wrong."
Richard P. Feynman
-
September 24th, 2014, 05:37 AM
#2
Re: Overloading std::distance for a specific iterator type
The idea is not that you are supposed to *specialize* distance (which is a function, and partial specialization is illegal anyways). Rather, you are supposed to *overload* it, and the use Koenig lookup rules to let the compiler chose the "best" candidate.
Exactly like you'd do with swap. You don't specialize it, you overload it:
Code:
#include <iterator>
template <typename T>
class Container
{
public:
class iterator : public std::iterator<std::forward_iterator_tag, T>
{
public:
friend long operator -(iterator lhs, iterator rhs)
{
return lhs.index - rhs.index;
}
friend long distance(const iterator& begin,
const iterator& end)
{
return end.index - begin.index;
}
private:
size_t index;
};
};
int main()
{
Container<int>::iterator ib;
Container<int>::iterator ie;
using std::distance;
long d = distance(ib, ie); // calls the specialised version.
return 0;
}
This works.
Unfortunately, just like swap, if the user an explicit "std::***(lhs, rhs)" directly, then you'll revert the default version.
I hope that helps.
Is your question related to IO?
Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
-
September 24th, 2014, 07:16 AM
#3
Re: Overloading std::distance for a specific iterator type
Thanks, that makes sense.
"It doesn't matter how beautiful your theory is, it doesn't matter how smart you are. If it doesn't agree with experiment, it's wrong."
Richard P. Feynman
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|