#include <type_traits>
#define NDEBUG

#include <iostream>
#ifndef NDEBUG
    #include <stdexcept>
    #include <sstream>
#endif

#define NV(v)#v<<" : "<<(v)
using ull = unsigned long long;

#ifdef NDEBUG
    #define ARR_NDB_NOEXCEPT noexcept
    #define ARR_NDB_CONSTEXPR constexpr
#else
    #define ARR_NDB_CONSTEXPR
    #define ARR_NDB_NOEXCEPT
#endif

template<size_t a,size_t b>
constexpr bool test(){
    return a>=b;
}

template<class T, size_t size>
class Array{
public:
private:
    T arr[size];
public:
    ARR_NDB_CONSTEXPR T& operator[](const size_t n)ARR_NDB_NOEXCEPT{
        #ifndef NDEBUG
            if(n>=size){
                std::stringstream ss;
                ss<<"\nYour [array size] is: "<<size<<"\nBut your [index] is :"<<n<<'\n';
                throw std::range_error(ss.str());
            }
        #endif
        return arr[n];
    }
    ARR_NDB_CONSTEXPR const T& operator[](const size_t n)const ARR_NDB_NOEXCEPT{
        #ifndef NDEBUG
            if(n>=size){
                std::stringstream ss;
                ss<<"\nYour [array size] is: "<<size<<"\nBut your [index] is :"<<n<<'\n';
                throw std::range_error(ss.str());
            }
        #endif
        return arr[n];
    }
    ARR_NDB_CONSTEXPR operator const T*()const ARR_NDB_NOEXCEPT{
        return arr;
    }
    constexpr bool is_const()const{
        return true;
    }
    constexpr bool is_const(){
        return false;
    }
};

constexpr size_t size {10};
constexpr Array<ull,size> arr{};
ull a;

int main(){
    std::cout<<NV(arr)<<'\n';
    // std::cout<<NV(&arr[size-1])<<'\n'<<NV(&a)<<'\n';
    // std::cout<<NV(a)<<'\n';
    // std::cout<<NV(++arr[size])<<'\n';
    // std::cout<<NV(a)<<'\n';
    constexpr ull a {arr[size-1]};
    const constexpr ull *p {arr};
    std::cout<<NV(a)<<'\n'<<NV(p)<<'\n';
}