2

I have a class named A, and in this class I have an iterable container, which I do iterate through following some rules of access -- order, emptiness, and others.

To simplify the following example, lets consider I'm just iterating through the container, but this cannot be done using the built-in container's iterator.

class A {
public:
    class iterator {
    public:
        // Constructor
        iterator() {
        }
        // Destructor
        ~iterator() {
        }
        // Advances the iterator
        void operator++() {
           // Some accessing policy
        }
    };
private:
    std::vector<int> a;
};

Everything works tremendously fine -- and looks very neat --, except that, when I do declare my iterator, I must use typename -- which I pretty much assume that is used in order to tell the compiler that what I have is a type, and not a class instanciation itself.

Questions:

  1. why do I have to use typename when I do:

    A a;
    for (typename A::iterator it(...); it != ...; ++it) {
    }
    
  2. How are iterators commonly defined, since a vector iterator does not require the typename tag? Does it have to do with declaring the vector from the class definition, instead of from the vector itself?

    std::vector<int> v;
    for (std::vector<int>::iterator it(v.begin()); it != v.end(); ++it) {
    }
    
  3. Are the iterators defined inside the container class -- I guess it's named composition --, or, if not, how is it iterators are added to the namespace of a class, like in:

    std::vector<int>::iterator it;
    
Rubens
  • 14,478
  • 11
  • 63
  • 92

2 Answers2

4

1 - why do I have to use typename when I do: [...]

You do not have to use typename. The typename disambiguator is required inside a template when you are using a dependent, qualified type name. This Q&A on StackOverflow clarifies things. This:

A a;
typename a::iterator it; // ERROR!

Is illegal C++. Provided that A is not the name of a template parameter, you should just do:

A::iterator it;

If you are inside a template and A is the name of a template parameter, such as in:

template<typename A>
struct X
{
    void foo()
    {
        typename A::iterator it;
    //  ^^^^^^^^
    //  This is necessary here!
    }
};

Then you have to use typename to tell the compiler that what follows the :: is the name of a type.


2 - How are iterators commonly defined, since a vector iterator does not require the typename tag?

Again, it is not true that "a vector iterator does not require the typename tag". If you have an explicit specialization of that vector, such as in:

std::vector<int>::iterator it; // "typename" not required

Then typename is not required, as it wasn't required in A::iterator it. If you are inside a template as in the following case, however, it will be required:

template<typename A>
struct X
{
    void foo()
    {
        typename std::vector<A>::iterator it;
    //  ^^^^^^^^
    //  This is necessary here!
    }
};

That's because std::vector<A>::iterator here is a qualified, dependent type name.


3 - Are the iterators defined inside the container class -- I guess it's named composition --, or, if not, how is it iterators are added to the namespace of a class, like in [..]

This could be done by defining a nested class, or simply done by using type aliases:

template<typename T>
struct X
{
    typedef T* iterator;
    iterator begin() { /* ... */ }
    // ...
};

X<int>::iterator it; // OK: "typename" not required

template<typename T>
void foo(X<T>& x)
{
    typename X<T>::iterator it = x.begin();
//  ^^^^^^^^
//  This is necessary here!

    // ...
}
Community
  • 1
  • 1
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • +1 Whoa! Very clarifying! Now I understand why `typename` was not required for the `std::vector::iterator`. Yes, I was actually using `template`s for `A::iterator`, and I didn't know `a::iterator` was illegal, since it pretty much worked (: Now I'll prettify my iterator definition with your explanation; I'm doing a DFS iterator, and it looks very neat this way ^^ Thanks for your reply! – Rubens Mar 09 '13 at 14:51
  • Sorry! I've just checked, and yes, I was using the class name itself, not the instance. I had something like: `typename Node::iterator it(...);`. Just adding this information, in case it may confuse someone else ^^ You've helped me a lot! (: – Rubens Mar 09 '13 at 14:54
  • I'm wondering here: how would I have a `operator++()` that would be called upon the iterator? I guess, in this particular case, only a class approach could solve this, right? – Rubens Mar 10 '13 at 03:32
1

There are a lot of problems with your example code, so this may be the answer you are looking for (waves hand) :) Of course if your examples are not correct, then all bets are off.

I'm surprised that this would work at all. 'a' is a variable, 'A' is the class.

Also, when declaring a variable with a default constructor, you do not use ending parenthesis ().

A a;
A::iterator it;
for (A::iterator it; it != ...; ++it) {
}

Also, iterators are defined within the container class. Using typename should only be necessary when you are dealing with templates and only when the accessing something that could be interpreted as either a static member or function/nested class or typedef. This can be further explained by the answer here which was also given by Andy Prowl.

Good luck

Community
  • 1
  • 1
Adrian
  • 10,246
  • 4
  • 44
  • 110
  • +1 I'm very sorry, I should have checked it first; actually, what I had was `typename A::iterator it`. I've just updated it; Thanks for your reply! – Rubens Mar 09 '13 at 15:30