So I tried to make a custom string & vector class (from a youtube channel named The Cherno). But when I tried to make a copy constructor on that vector class with my custom string class, I get this exception: str was nullptr (Occured in the string copy constructor) and also when I tried with a primitive data types (int) I also get this exception: std::move<int & __ptr64>(...) returned nullptr (Occured on the re_allocate function).
main.cpp:
int main() {
    utils::list<int> list;
    list.place(5);
    utils::list<int> other = list; // Works fine if I remove this line
}
String copy constructor:
utils::string::string(const string& other)
    : pr_Length(other.pr_Length)
{
    // If the other string was not initialized then return
    if (!other.pr_Init) {
        pr_Init = false;
        return;
    }
    // Allocates a new char*
    pr_Data = new char[pr_Length + 1];
    // Copy all the char* on other string and copy it here
    std::memcpy(pr_Data, other.pr_Data, pr_Length + 1);
    // This string is initialized
    pr_Init = true;
}
Vector class:
template<typename T>
    class list {
    private:
        T* pr_Array;
        size_t pr_Size;
        size_t pr_Cap;  
    public:
        using ValType = T;
        using Iterator = list_iterator<list<T>>;
        list() {
            re_allocate(2);
        }
        ~list() {
            destroy_all();
            ::operator delete(pr_Array, pr_Cap * sizeof(T));
        }
        list(const list& other)
            : pr_Size(other.pr_Size), pr_Cap(other.pr_Cap)
        {
            re_allocate(pr_Cap);
        }
        void add(const T& elem) {
            if (pr_Size >= pr_Cap) {
                re_allocate(pr_Cap + 2);
            }
            new(&pr_Array[pr_Size]) T(std::move(elem));
            pr_Size++;
        }
        void add(T&& elem) {
            if (pr_Size >= pr_Cap) {
                re_allocate(pr_Cap + 2);
            }
            new(&pr_Array[pr_Size]) T(std::move(elem));
            pr_Size++;
        }
        template<typename...Args>
        T& place(Args&&... args) {
            if (pr_Size >= pr_Cap) {
                re_allocate(pr_Cap + 2);
            }
            new(&pr_Array[pr_Size]) T(std::forward<Args>(args)...);
            return pr_Array[pr_Size++];
        }
        void destroy_back() {
            if (pr_Size == 0) {
                return;
            }
            pr_Array[pr_Size].~T();
            pr_Size--;
        }
        void destroy_all() {
            for (size_t i = 0; i < pr_Size; i++) {
                pr_Array[i].~T();
            }
            pr_Size = 0;
        }
        const T& operator[](size_t i) const {
            return pr_Array[i];
        }
        T& operator[](size_t i) {
            return pr_Array[i];
        }
        const size_t size() const {
            return pr_Size;
        }
        size_t size() {
            return pr_Size;
        }
        Iterator begin() {
            return Iterator(pr_Array);
        }
        Iterator end() {
            return Iterator(pr_Array + pr_Size);
        }
    private:
        void re_allocate(size_t cap) {
            T* newBlock = (T*)::operator new(cap * sizeof(T));
            if (cap < pr_Size) {
                pr_Size = cap;
            }
            for (size_t i = 0; i < pr_Size; i++) {
                new(newBlock + i) T(std::move(pr_Array[i]));
            }
            for (size_t i = 0; i < pr_Size; i++) {
                pr_Array[i].~T();
            }
            ::operator delete(pr_Array, pr_Cap * sizeof(T));
            pr_Array = newBlock;
            pr_Cap = cap;
        }
    };
 
    