Почему при выводе динамического массива в консоль не нужен оператор разыменования?

Прохожу тему динамических массивов и при написании кода стало интересно, почему при выводе динамического массива не нужен оператор разыменования? Почему нужен в арифметике указателей я понял.

#include<iostream>
using namespace std;

/*
*   динамический массив
*/



void main()
{
    int size = 0;
    cout << "enter value " << endl;
    cin >> size;
    int* arr = new int[size];//выделение динамической памяти для массива

    for (int i = 0; i < size; i++)//Заполнение динамического массива
    {
        arr[i] = rand();
    }

    for (int i = 0; i < size; i++)//Вывод динамического массива
    {
        cout << arr[i] << "\t";
        cout << *(arr + i) << endl; //арифметика указателей
    }
    delete [] arr;
}

Ответы (2 шт):

Автор решения: Harry

По сути выражение arr[i] — это и есть *(arr + i).

Разыменовывать можно указатель, но arr[i] таковым не является.

arr[i] — это i-ый элемент массива, начинающегося по адресу arr. Т.е. элемент, находящийся по адресу arr + i. А чтобы получить значение по этому адресу, нужно разыменование — *(arr + i).

→ Ссылка
Автор решения: CameL

Столкнулся с таким же вопросом, провел небольшое исследование на основе ответа на аналогичный вопрос на en stackoverflow

int main()
{
    int* a = new int[5];
    for (int i = 0; i < 4; ++i) {
        a[i] = i + 1;

    }
    int x = 10;
    int *p1 = &x;
    int *p2 = &x;
    
    // Эти выражения эквивалентны
    *p1 = 20;
    p2[0] = 20;
    
    // Выводы тоже эквивалентны
    cout << *(p1) << endl;
    cout << p2[0] << endl;
    
    // Попробуем с массивом
    cout << *(a) << endl;
    cout << a[0]<<endl;
    
    // Если не первый элемент
    cout << *(a + 3) << endl;
    cout << a[3]<<endl;
    return 0;
}

В стандарте C++ ISO/IEC 14882:2014 п 5.2.1 написано:

The expression E1[E2] is identical (by definition) to *((E1)+(E2))

Выражение E1[E2] идентично (по определению) *((E1)+(E2))

Таким образом можно утверждать, что записи

cout << *(a + 3) << endl;
cout << a[3]<<endl;

аналогичны, и разыменование не нужно

→ Ссылка