Nested class "does not name a type"

Viewed 266

I'm getting the following error:

main.cpp:18:5: error: 'Iterator' does not name a type
   18 |     Iterator begin() {
      |     ^~~~~~~~

With this code:

#include <iostream>
#include <iostream>
#include <memory>
#include <fstream>
#include <filesystem>

using namespace std;

class Numbers {
    private:
    int current;
    int end;

    public:

    Numbers(int end) : current(0), end(end) {}

    Iterator begin() {
        return Iterator(this);
    }

    bool operator==(const Numbers& other) const {
        return current == other.current && end == other.end;
    }

    bool operator!=(const Numbers& other) const {
        return !(other == *this);
    }

    class Iterator {
        private:
        Numbers* range;

        public:
        using value_type = int;
        using difference_type = ptrdiff_t;
        using pointer = int*;
        using reference = int&;
        using iterator_category = input_iterator_tag;

        Iterator(Numbers* range) : range(range) {}

        int operator*() const {
            return range->current;
        }

        int* operator->() const {
            return &range->current;
        }

        bool operator==(const Iterator& other) const {
            return other.range == range;
        }

        bool operator!=(const Iterator& other) const {
            return !(*this == other);
        }

        Iterator& operator++() {
            range->current++;
            return *this;
        }


    };
};

It turns out that moving the begin function under the nested Iterator class makes this compile.

But it's odd - don't nested classes follow the same access rules as any other member, meaning no need for forward-references?

I searched the other questions on the site regarding this exact issue, didn't seem to find an answer.

1 Answers

From a comment to the question

Also - I have no problem calling member function f in member function q where f is defined after q. Can you explain why the latter example is different than the situation described in this question?

According to the C++ 20 Standard (11.4 Class members)

6 A complete-class context of a class is a

> (6.1) — function body (9.5.1),

(6.2) — default argument (9.3.3.6),

(6.3) — noexcept-specifier (14.5), or

(6.4) — default member initializer

within the member-specification of the class

So inside the complete class context the name of the function f is visible within the body of the function q.

However the inner class is declared outside the complete class context. So according to the C++ 20 Standard (6.5.1 Unqualified name lookup)

7 A name used in the definition of a class X23 outside of a complete-class context (11.4) of X shall be declared in one of the following ways:

> (7.1) — before its use in class X or be a member of a base class of X (11.8), or ...

So the name of the inner class Iterator must be declared before its using as a return type of a member function.

Instead of the name Iterator you could use for example the place holder auto as the return type.

auto begin() {
    return Iterator(this);
}
Related