#include <iostream>
#include <type_traits>

// Klasse implementiert einen gesicherten Zugriff auf
// ein Feld. Wird beim Schreiben ueber das Ende des Feldes
// zugegriffen, so wird das Feld entsprechend erweitert
// Standardmaessig wird das Feld fuer die Ablage von int-
// Daten angelegt
// ACHTUNG!! Werden in dem Feld Objektzeiger abgelegt
// so muessen die dazugehörigen Objekte explizit wieder
// geloescht werden.
template <typename T = int>
class DynArray
{
    T   *data;  // Zeiger auf Datenfeld
    int size;   // Groesse des Datenfelds
  public:
    // ctor, reserviert ein Feld mit einer bestimmten
    // Anzahl von Elementen (Standard 5 Elemente)
    DynArray(int reserveData = 5);
    // dtor
    ~DynArray();
    // Indexoperator fuer Array-Zugriff
    T& operator[] (int index);
    // Groesse des Feldes zurueckgeben
    int GetSize();
    // Ausgabe des DynArrays
    void PrintArray();
};
// ctor
// Standardmaessig wird ein Feld mit der Groesse 5 allokiert
template <typename T>
DynArray<T>::DynArray(int reserveData)
{
    // Falls Datenfeld angelegt werden soll
    if (reserveData)
    {
        // Datenfeld reservieren
        data = new T[reserveData];
        size = reserveData;
        // Wenn T ein arithmetischer Datentyp (Ganzzahl-/Gleichkomma-Datentyp)
        // ist, Speicher mit 0 initialisieren
        if  constexpr (std::is_arithmetic_v<T>)
            for (auto index=0; index<size; index++)
                data[index] = 0;
    }
}
// dtor, gibt reserviertes Feld frei
template <typename T>
DynArray<T>::~DynArray()
{
    delete [] data;
}
// Ueberladener Indexoperator fuer den Zugriff
// auf die Elemente im DynArray
template <typename T>
T& DynArray<T>::operator[] (int index)
{
    // Falls Index groesser als die Anzahl der Feldelement
    if (index>=size)
    {
        // Neues Feld mit der Groesse 'Index+1' reservieren
        // +1 hier unbedingt notwendig, da z.B. der
        // Index 10 das 11. Element adressiert!
        T *newData = new T[index+1];
        // Bisherige Daten in neues Feld uebernehmen
        // mit move-Zuweisungsoperator
        for (int copyOld = 0; copyOld < size; copyOld++)
            newData[copyOld] = std::move(data[copyOld]);

        // Wenn T ein arithmetischer Datentyp (Ganzzahl-/Gleichkomma-Datentyp)
        // ist, Speicher mit 0 initialisieren
        if constexpr (std::is_arithmetic_v<T>)
            for (auto newIndex = size; newIndex<index; newIndex++)
                newData[newIndex] = 0;
        // Altes Feld nun entfernen
        delete [] data;
        // Und Zeiger auf neues Feld abspeichern
        data = newData;
        // Nicht vergessen: die Groesse korrigieren
        size = index+1;
    }
    // Gewuenschtes Element zurückliefern
    return data[index];
}
template <typename T>
inline int DynArray<T>::GetSize()
{
    return size;
}
// Komplettes Feld ausgeben
template <typename T>
void DynArray<T>::PrintArray()
{
    for (int index = 0; index < size; index++)
        std::cout << data[index] << ", ";
    std::cout << '\n';
}
