Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I inherit from std::iterator, but compiler does not recognise 'pointer' or 'reference'

I am in the process of making a custom iterator, but I am puzzled on what makes the compiler not recognise the pointer or reference typedefs in the case of a templated iterator implementation.

The following code compiles fine:

#include <iterator>

struct It : std::iterator<std::random_access_iterator_tag, int>
{
    int *v;
    pointer operator-> () { return v; }
    reference operator* () { return *v; }

};

but once I templatize my custom iterator I get errors:

#include <iterator>

template <class T>
struct It2 : std::iterator<std::random_access_iterator_tag, T>
{
    T *v;
    pointer operator-> () { return v; }
    reference operator* () { return *v; }

};

whole code here

error: 'pointer' does not name a type
note: (perhaps 'typename std::iterator<std::random_access_iterator_tag, T>::pointer' was intended)
error: 'reference' does not name a type
...

1> Why can't the compiler see the pointer and reference definitions contained in the std::iterator ?

According to the note, it seems that I shouldn't have bothered using the std::iterator struct, but instead I should have manually copied the typedefs. But that seems too error prone in case the iterator or iterator_traits get an extra typedef in the future.

2. How do you think I should deal with the definition of these traits (pointer, reference etc.) ?

like image 818
Grim Fandango Avatar asked Mar 22 '26 00:03

Grim Fandango


2 Answers

1> Why can't the compiler see the pointer and reference definitions contained in the std::iterator ?

Because the compiler cannot rule out you attempting to provide a specialisation of std::iterator where pointer and reference aren't type definitions, dependent names can never be interpreted as a typedef at template definition time.

2. How do you think I should deal with the definition of these traits (pointer, reference etc.) ?

While explicitly qualifying them with typename every time works, what you can do instead is declare them once as such, and rely on that one declaration in the rest of your It2 definition:

#include <iterator>

template <class T>
struct It2 : std::iterator<std::random_access_iterator_tag, T>
{
    using base = std::iterator<std::random_access_iterator_tag, T>;
    using typename base::pointer;
    using typename base::reference;
    T *v;
    pointer operator-> () { return v; }
    reference operator* () { return *v; }
};

Now that your base class is a dependent type, so are its members pointer and reference.

You need to:

  • indicate to the compiler that those words are types
  • indicate to the compiler that those words are member types

You can do it easily:

   typename It2::pointer operator-> () { return v; }
   typename It2::reference operator* () { return *v; }
// ^^^^^^^^^^^^^^

Why? Because C++ is quirky as frak and extremely inexpressive.

like image 29
Lightness Races in Orbit Avatar answered Mar 24 '26 12:03

Lightness Races in Orbit



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!