Heisenbug Heisenbug - 2 months ago 13
C++ Question

Correct type declaration for method returning iterator to C array

I can iterate through a C-style array this way:

char foo[3] = { 'a', 'b', 'c' };

for (auto it = std::begin(foo); it != std::end(foo); ++it)
{
*it = 'k'; //values of foo are correctly modified
}


Now suppose I want to wrap the array inside a class, and expose a
begin()
and
end()
methods that return the relative iterators.
I tried the following:

template<size_t size>
class StackMemPolicy
{
private:
char mem[size];

public:
typedef typename std::iterator<std::input_iterator_tag, char> iter;
iter begin()
{
return std::begin(mem);
}
iter end()
{
return std::end(mem);
}
}


It seems the returned type declaration is wrong, and the following calling code don't compile:

StackMemPolicy<4> bar;
for (auto it = bar.begin(); it != bar.end(); ++it)
{
*it = 'k';
}


The error is the following:


Error 1 error C2678: binary '!=' : no operator found which takes a
left-hand operand of type 'StackMemPolicy<4>::iter' (or there is no
acceptable conversion)


Can anyone tell me where's my error?

Answer

std::iterator is meant to be used as a base class. From 24.4.2/1:

namespace std {
  template<class Category, class T, class Distance = ptrdiff_t,
    class Pointer = T*, class Reference = T&>
  struct iterator {
    typedef T value_type;
    typedef Distance difference_type;
    typedef Pointer pointer;
    typedef Reference reference;
    typedef Category iterator_category;
  };
}

It only gives you some typedefs and won't magically implement all the required operators. In your case, begin() and end() should probably just return char*, which already has specializations for std::iterator_traits.

However, if your iterator has to be smarter (maybe this is some sort of circular buffer, for example), you would have to create your own iterator class and implement the required operators. For that iterator to work with various features in the standard library (such as std::iterator_traits), you'll need predefined typedefs such as value_type, iterator_category, etc.

Since getting those right can sometimes be tricky, std::iterator will define those for you based on the given template parameters. Here's an example of using std::iterator_traits with an iterator.

Note that as of C++17, std::iterator has been deprecated for various reasons. This post has some workarounds.