Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 702
Heisenbug Avatar asked Jun 01 '15 15:06

Heisenbug


3 Answers

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.

like image 66
isanae Avatar answered Sep 20 '22 21:09

isanae


It works for me if I change:

typedef typename  std::iterator<std::input_iterator_tag, char> iter;

to:

typedef char * iter;
like image 40
Paul R Avatar answered Sep 21 '22 21:09

Paul R


Unless you don't want to modify the behavior, you should just return std::begin(mem) which is nothing more than a char *.

See see 2'nd overload

like image 22
bolov Avatar answered Sep 21 '22 21:09

bolov