Basic iterator supporting vector implementation On the fly, I just implemented this iterator supporting vector class to understand how range-based for loop works.
It's really basic but let me know what can be improved.

Vector.h

#ifndef VECTOR_H
#define VECTOR_H

#include &lt;stdexcept&gt;

template &lt;typename T&gt;
class Vector
{
public:
    /* The iterator */
    class Iterator
    {
    public:
        Iterator(const Vector&lt;T&gt; *vector, int nIndex);
        const T &amp;operator*() const;
        Iterator &amp;operator++();
        bool operator!=(const Iterator &amp;other) const;

    private:
        const Vector&lt;T&gt; *m_pVector;
        int m_nIndex = -1;
    };

public:
    // constructors
    Vector() = default;
    explicit Vector(int nSize);

    ~Vector();

    void insert(const T &amp;value);

    int size() const;
    const T &amp;operator[](int nIndex) const;

    Iterator begin() const;
    Iterator end() const;

private:
    T *m_pData = nullptr;
    int m_nSize = 0;
    int m_nCapacity = 0;
};

/*
 * Vector methods
 **/
template &lt;typename T&gt;
Vector&lt;T&gt;::Vector(int nCapacity)
{
    m_nCapacity = nCapacity;
    m_pData = new T[m_nCapacity];
}

template &lt;typename T&gt;
Vector&lt;T&gt;::~Vector()
{
    delete m_pData;
    m_nSize = 0;
    m_nCapacity = 0;
}

template &lt;typename T&gt;
void Vector&lt;T&gt;::insert(const T &amp;value)
{
    if (m_nSize == m_nCapacity)
    {
        if (m_nCapacity == 0)
            m_nCapacity = 1;

        m_nCapacity *= 2;

        // allocate 2x larger memory
        auto pNewMemory = new T[m_nCapacity];

        // copy data to there
        for (auto idx = 0; idx &lt; m_nSize; ++idx)
            pNewMemory[idx] = m_pData[idx];

        delete m_pData;
        m_pData = pNewMemory;
    }

    // insert the new element
    m_pData[m_nSize] = value;
    ++m_nSize;
}

template &lt;typename T&gt;
int Vector&lt;T&gt;::size() const
{
    return m_nSize;
}

template &lt;typename T&gt;
const T &amp;Vector&lt;T&gt;::operator[](int nIndex) const
{
    if (nIndex &gt;= m_nSize)
        throw std::exception("Index out of range");

    return m_pData[nIndex];
}

template &lt;typename T&gt;
typename Vector&lt;T&gt;::Iterator Vector&lt;T&gt;::begin() const
{
    return Vector&lt;T&gt;::Iterator{ this, 0 };
}

template &lt;typename T&gt;
typename Vector&lt;T&gt;::Iterator Vector&lt;T&gt;::end() const
{
    return Vector&lt;T&gt;::Iterator{ this, m_nSize };
}

/*
 * Iterator methods
 **/
template &lt;typename T&gt;
Vector&lt;T&gt;::Iterator::Iterator(const Vector&lt;T&gt; *pVector, int nIndex)
    : m_pVector(pVector)
    , m_nIndex(nIndex)
{
}

template &lt;typename T&gt;
const T &amp;Vector&lt;T&gt;::Iterator::operator*() const
{
    return m_pVector-&gt;operator[](m_nIndex);
}

template &lt;typename T&gt;
typename Vector&lt;T&gt;::Iterator &amp;Vector&lt;T&gt;::Iterator::operator++()
{
    ++m_nIndex;
    return *this;
}

template &lt;typename T&gt;
bool Vector&lt;T&gt;::Iterator::operator!=(const Vector&lt;T&gt;::Iterator &amp;other) const
{
    return m_nIndex != other.m_nIndex;
}

#endif // !VECTOR_H


main.cpp

#include &lt;iostream&gt;

#include "Vector.h"

int main()
{
    try
    {
        Vector&lt;int&gt; vector;
        vector.insert(8);
        vector.insert(3);
        vector.insert(1);
        vector.insert(7);
        vector.insert(2);

        for (auto i : vector)
            std::cout &lt;&lt; i &lt;&lt; std::endl;
    }
    catch (const std::exception &amp;e)
    {
        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
    }

    std::cin.get();
    return 0;
}
The range-based for loop
This works via the begin() and end() functions.
So for any class X if there are stand alone functions begin(X&amp;) and end(X&amp;) the range based for will work. Normally this will default to the std::begin() and std::end() but if you have written explicit versions of these functions in the same namespace as X these will be used.
Example:
#include &lt;iostream&gt;

namespace Y
{
    class X
    {
        public:
            char data[14] = &quot;This is text&quot;;
    };

    char* begin(X&amp; obj) {
        return obj.data;
    }

    char* end(X&amp; obj) {
        return obj.data + 10;
    }
}

int main()
{
    Y::X   x;
    for(auto const&amp; a: x) {
        std::cout &lt;&lt; a;
    }
}

OK. So what happens if you don't write your own begin() and end() functions?
In this case it will use the standard versions of these functions. std::begin() which will call the begin() method on the object and std::end() which will call the end() method on the object.
Example 2:
#include &lt;iostream&gt;

namespace Y
{
    class X
    {
            char data[14] = &quot;This is text&quot;;
        public:
            char* begin() {
                return data;
            }

            char* end() {
                return data + 10;
            }
    };
}

int main()
{
    Y::X   x;
    for(auto const&amp; a: x) {
        std::cout &lt;&lt; a;
    }
}

How to think of range based for
You can think of ranged based for as a compiler shortcut.
for(&lt;Type&gt; &lt;Var&gt; : &lt;Obj&gt;) {
    &lt;CODE&gt;
}

Is just short hand for:
{
    using std::begin;
    using std::end;

    auto __end = end(&lt;Obj&gt;);

    for(auto __loop = begin(&lt;Obj&gt;); __loop != __end; ++__loop) {
        &lt;Type&gt; &lt;Var&gt; = *__loop;
        &lt;CODE&gt;
    }
}

Implementation of Iterators
The implementation of an iterator is based on the &quot;Iterator Concept&quot;. There are actually five types of iterator in C++. But since you are using vector as your example implementation we should consider the &quot;Random Access Iterator Concept&quot;.
To qualify as a random access iterator you have to uphold a specific contract. Here is some documentation on it.
https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator
Not only does the iterator need to define certain methods (with specific requirements on how they work together). But your type also needs to define certain types via std::iterator_traits.
Types
You need to define the following types:

value_type:
When the iterator is de-referenced this is the type you get.
difference_type:
When you subtract two iterators this is the type of the result
pointer:
A type that points at type value_type
reference:
A type that can be used as a reference to a value of type value_type
iterator_category:
A type that represents the category of the iterator.

So you could have written your own specialization of the iterator_traits class
namespace std
{
    template&lt;typename T&gt;
    class iterator_traits&lt;Vector&lt;T&gt;::Iterator&gt;
    {
        using value_type = T;
        using difference_type = std::ptrdiff_t;
        using pointer = T*;
        using reference  = T&amp;;
        using iteratory_category = std::random_access_iterator_tag;
    };
}

But that is a little bit like actual hard work. The designers of the STL actually got smart and the default implementation of this type refer back to the iterator type to get these types. So you can define these types in your iterator (and not define a specialization of the iterator_traits class).
Rather do this:
template&lt;typename T&gt;
class Vector
{
    class Iterator
    {
        using value_type = T;
        using difference_type = std::ptrdiff_t;
        using pointer = T*;
        using reference  = T&amp;;
        using iterator_category = std::random_access_iterator_tag;
    };
    ....
}

Note: If you follow the advice above and use a pointer as your iterator. The iterator_traits for pointer types are well defined in the standard and no work is required.
Properties of an Iterator
The basic properties of an iterator are:

Default Constructible    *** Currently not supported.
Copy Constructible
Copy Assignable
Destructible
Swappable      *** Currently not supported.
Equality Comparable  *** Currently not supported (you need ==)
objects returned via de-referencing need to be mutable (not supported)

Supported actions for a random access iterator:
*i                  // returns the current value
*i  = v             // Assign value `v` to the value dererenced.
++i                 // increments the iterator (returns a reference)
--i                 // See above
i++                 // increments the iterator (but returns a reference to the original)
i--                 // See above
*i++                // returns the current value and increments the iterator
*i--                // See above

i += n              // Moves the iterator forward
i -= n              // Same as last one
i + n               // Creates a new iterator moved forward from i
n + i               // Same as last one
i - n               // Same as last one

i1 - i2             // returns the distance between the iterators.

i[n]                // returns the item n steps forward from this iterator.
i-&gt;m                // Access the member `m` referenced by i

i1 &lt;  i2            // Compare iterators
i1 &gt;  i2
i1 &lt;= i2
i1 &gt;= i2
i1 == i2
i1 != i2

OK So I have dumped an all lot of requirements on iterators here. But it is not as bad as it seems. Have a look at an iterator I built for a stack overflow question.
https://stackoverflow.com/a/1120224/14065
This iterator is only a forward iterator (so it has fewer requirements). But it shows you how to implement the main basic requirements very quickly. The rest should not be too hard to implement for random accesses iterator.
Got bored so here:
class Iterator
{
public:
    using value_type = T;
    using pointer    = T*;
    using reference  = T&amp;;
    using difference_type = std::ptrdiff_t;
    using iterator_category = std::random_access_iterator_tag;

    Iterator():                    v(nullptr), i(0) {}   
    Iterator(Vector&lt;T&gt;* v, int i): v(v),       i(i) {}
    // Default Copy/Move Are Fine.
    // Default Destructor fine.

    reference       operator*()             {return (*v)[i];}
    const reference operator*()       const {return (*v)[i];}
    pointer         operator-&gt;()            {return &amp;((*v)[i]);}
    const pointer   operator-&gt;()      const {return &amp;((*v)[i]);}
    reference       operator[](int m)       {return (*v)[i + m];}
    const reference operator[](int m) const {return (*v)[i + m];}
    

    Iterator&amp; operator++()       {++i;return *this;}
    Iterator&amp; operator--()       {--i;return *this;}
    Iterator  operator++(int)    {Iterator r(*this);++i;return r;}
    Iterator  operator--(int)    {Iterator r(*this);--i;return r;}

    Iterator&amp; operator+=(int n)  {i += n;return *this;}
    Iterator&amp; operator-=(int n)  {i -= n;return *this;}

    Iterator operator+(int n)   const {Iterator r(*this);return r += n;}
    Iterator operator-(int n)   const {Iterator r(*this);return r -= n;}

    difference_type operator-(Iterator const&amp; r) const {return i - r.i;}

    // Note: comparing iterator from different containers
    //       is undefined behavior so we don't need to check
    //       if they are the same container.
    bool operator&lt;(Iterator const&amp; r)  const {return i &lt;  r.i;}
    bool operator&lt;=(Iterator const&amp; r) const {return i &lt;= r.i;}
    bool operator&gt;(Iterator const&amp; r)  const {return i &gt;  r.i;}
    bool operator&gt;=(Iterator const&amp; r) const {return i &gt;= r.i;}
    bool operator!=(const Iterator &amp;r) const {return i != r.i;}
    bool operator==(const Iterator &amp;r) const {return i == r.i;}

private:
    Vector&lt;T&gt;* v;
    int        i;
};

The Container.
Since you were asking about Iterators I will not give input on the Vector just yet (but lets say it needs work).
I have written a couple of articles on implementing the vector that you can have a look at.
http://lokiastari.com/series/