How to write a C++11 template that can take a const iterator
c++, c++-concepts, c++11, templates
Solution
template <class ConstIt>
It's that simple. There's nothing to be enforced on the caller side here, as a non-`const` iterator is also usable for `const` access, so it's just API documentation, and that's what your choice of parameter identifier is - API documentation.
That does lead on to the question of enforcement on the callee/function side - so it can't be pretending it will only use the iterator for `const` access then modify elements anyway. Should you care about that, you could accept the parameter using some identifier making it clear it wasn't meant to be used everywhere throughout the function, then create a `const_iterator` version with a more convenient identifier. That could be tricky as in general you don't know if the iterator type is a member of a container, let alone what that container type is and whether it has a `const_iterator` too, so some manner of Concepts would indeed be ideal - fingers crossed for C++14. Meanwhile:
- have your caller tell you the container type,
- write your own traits, OR
- write a simple wrapper class that holds an iterator and ensures only `const` access to the referenced data escapes the interface
This last wrapper approach is illustrated below (not all of the iterator API is implemented so flesh out as needed):
template <typename Iterator>
class const_iterator
{
public:
typedef Iterator iterator_type;
typedef typename std::iterator_traits<Iterator>::difference_type difference_type;
// note: trying to add const to ...:reference or ..:pointer doesn't work,
// as it's like saying T* const rather than T const* aka const T*.
typedef const typename std::iterator_traits<Iterator>::value_type& reference;
typedef const typename std::iterator_traits<Iterator>::value_type* pointer;
const_iterator(const Iterator& i) : i_(i) { }
reference operator*() const { return *i_; }
pointer operator->() const { return i_; }
bool operator==(const const_iterator& rhs) const { return i_ == rhs.i_; }
bool operator!=(const const_iterator& rhs) const { return i_ != rhs.i_; }
const_iterator& operator++() { ++i_; return *this; }
const_iterator operator++(int) const { Iterator i = i_; ++i_; return i; }
private:
Iterator i_;
};
Sample usage:
template <typename Const_Iterator>
void f(const Const_Iterator& b__, const Const_Iterator& e__)
{
const_iterator<Const_Iterator> b{b__}, e{e__}; // make a really-const iterator
// *b = 2; // if uncommented, compile-time error....
for ( ; b != e; ++b)
std::cout << *b << '\n';
}
See it running at ideone.com here.
Problem
In responding to this question on CodeReview, I was thinking about how one might write a template function to indicate `const`-ness of a contained object. To be specific, consider this templated function ``` #include <iostream> #include <numeric> #include <vector> template <class It> typename std::iterator_traits<It>::value_type average(It begin, It end) { typedef typename std::iterator_traits<It>::value_type real; real sum = real(); unsigned count = 0; for ( ; begin != end; ++begin, ++count) sum += *begin; return sum/count; } int main() { std::vector<double> v(1000); std::iota(v.begin(), v.end(), 42); double avg = average(v.cbegin(), v.cend()); std::cout << "avg = " << avg << '\n'; } ``` It takes an iterator and calculates an average based on the contained numbers, but it is guaranteed not to modify the vector through the passed iterators. How does one convey this to a user of the template? Note that declaring it like this: ``` template <class It> typename std::iterator_traits<It>::value_type average(const It begin, const It end) ``` doesn't work because it's not the iterator, but the thing the iterator points to, that's `const`. Do I have to wait for concepts to be standardized? Note that I don't want to require const iterators, but instead to indicate that they may be safely used here. That is, rather than restricting the caller, I want to convey a promise that my code is making: "I will not modify your underlying data."