I can not get the idea of how to create Array template class properly in C++.
The problem is solely out of learning purposes.
Let me provide the code first.
Array.h :
//Developed by Trofimov Yaroslav on 30.03.2018
#ifndef _ARRAY_H_TROFIMOV_
#define _ARRAY_H_TROFIMOV_
#include <string>
template<const size_t n, typename T>
class Array {
    static unsigned __freeId, __quantity;
    unsigned _id;
    T** _array;
    const size_t _n;
public:
    typedef const bool (* const BooleanResultDelegate)(const T&);
    class ArrayError {
        const std::string _reason;
        const size_t _index;
        const size_t _maxIndex;
    public:
        ArrayError(const size_t index, const size_t maxIndex,const std::string& reason = "") 
            : _index(index), _maxIndex(maxIndex), _reason(reason) {}
        std::string explanation(void) {
            std::string res += "Index: " + std::to_string(_index) + "\n"; 
            res += "Max index: " + std::to_string(_maxIndex) + "\n"; 
            res += "Reason: " + _reason + "\n"; 
            return res;
        }
    };
    explicit Array<n, T>(T* arrayFiller = 0) 
        : _n(n), _array(new T*[n]), _id(++__freeId) {
            if(arrayFiller != 0) {
                for(size_t i(0); i < length(); ++i) {
                    _array[i] = new T(*arrayFiller);
                }
            } else {
                for(size_t i(0); i < length(); ++i) {
                    _array[i] = arrayFiller;
                }
            }
            reportIfDebug<n, T>(*this, "created");
            ++__quantity;
    }
    explicit Array<n, T>(const T& arrayFiller) 
        : _n(n), _array(new T*[n]), _id(++__freeId) {
            for(size_t i(0); i < length(); ++i) {
                _array[i] = new T(arrayFiller);
            }
            reportIfDebug<n, T>(*this, "created");
            ++__quantity;
    }
    Array<n, T>(const Array<n, T>& that) 
        : _n(n), _array(new T[n]), _id(++__freeId) {
            for(size_t i(0); i < length(); ++i) {
                (*this)[i] = new T[that[i]];
            }
            reportIfDebug<n, T>(*this, "created");
            ++__quantity;
    }
    ~Array<n, T>(void) {
        removeAll();
        delete [] _array; _array = 0;
        reportIfDebug<n, T>(*this, "deleted", false);
        --__quantity;
    }
    T* operator[](const size_t i) {
        if(i > length()) {
            throw ArrayError(i, _n, "out of bounds exception");
        }
        return _array[i];
    }
    const T* operator[](const size_t i) const {
        if(i > length()) {
            throw ArrayError(i, _n, "out of bounds exception");
        }
        return _array[i];
    }
    const size_t length() const {
        return _n;
    }
    const unsigned getID() const {
        return _id;
    }
    void removeAll(BooleanResultDelegate removeCondition = 0) {
        for(size_t i(0); i < length(); ++i) {
            if(removeCondition == 0 || removeCondition(*_array[i])) {
                delete [] _array[i]; _array[i] = 0;
            }
        }
    }
};
template<const size_t n, typename T>
unsigned Array<n, T>::__freeId(0);
template<const size_t n, typename T>
unsigned Array<n, T>::__quantity(0);
template<const size_t n, typename T>
void reportIfDebug(
    const Array<n, T>& instance, 
    const char* const message, 
    const bool showContent = true) {
#ifndef NDEBUG
    std::cout << "========================================" << std::endl;
    std::cout << typeid(instance).name() << ' ' 
        << message << ' '
        << "id: " << instance.getID() << std::endl;
    if(showContent) {
        std::cout << instance;
    }
    std::cout << "========================================" << std::endl;
#endif
}
template<const size_t n, typename T>
std::ostream& operator<<(std::ostream& os, const Array<n, T>& instance) {
    for(size_t i(0); i < instance.length(); ++i) {
        if(instance[i] == 0) {
            os << "[" << i << "]: " << instance[i] << "\n";
        } else {
            os << "[" << i << "]: " << *instance[i] << "\n";
        }
    }
    return os;
}
#endif
Main.cpp :
//Developed by Trofimov Yaroslav on 30.03.2018
#include <iostream>
#include "Array.h"
int main(void) {
    const Array<5, int> a(7);
    std::cout << *a[2] << std::endl;
    return 0;
}
What is the main problem right now - is that the client of my Array class would have to use [indirection operator *] and [0 value pointer check] to use the objects from array.
I do not want the client to do that. But, if I use reference instead of pointer as a return type of operator[] I will not be able to return types which do not have copy constructor and I will return trash if nothing was put there before.
It seems like in Java and C# the problem is not fixed as well. The user is getting a reference to an object there and definitely should check for null being returned. And [indirection operator *] is called automatically there.
