Я сделал для своего класса, который работает с массивом в персистентном виде, undo-redo механизм. Можешь проверить мой код на правильность реализации?
Это изначальная версия:

template <typename T>
class PersistentArray
{
private:
    std::vector<std::vector<T>> versions{};
  
public:
    PersistentArray(T* arr, int size)
    {
        std::vector<T> base(arr, arr + size);
        versions.push_back(base);
    }

    void addVersion(int root_position, int change_index, T new_value)
    {
        if (root_position < 0 || root_position >= versions.size() ||
            change_index < 0 || change_index >= versions[root_position].size()) 
        {
            std::cout << "Invalid root position or change index!" << std::endl;
            return;
        }
        
        auto temp = versions[root_position]; 
        temp[change_index] = new_value;     
        versions.push_back(temp);              
    }
    
    void printAllVersions()
    {
	    for (size_t i = 0; i < versions.size(); i++) 
	    {
            std::cout << "Version [" << i << "]: \t{";
            for (size_t j = 0; j < versions[i].size(); j++) 
            {
                std::cout << "'" << versions[i][j] << "'";
                if (j < versions[i].size() - 1) 
                {
                    std::cout << ", ";
                }
            }
            std::cout << "}\n";
        }
    }
};

А это после добавления механизма undo-redo:
template <typename T>
class PersistentArray 
{
private:
    std::vector<std::vector<T>> versions;
    std::stack<std::pair<int, int>> undo_stack;  // (root_position, change_index)
    std::stack<std::pair<int, int>> redo_stack;  // (root_position, change_index)

public:
    PersistentArray(T* arr, int size) 
    {
        std::vector<T> base(arr, arr + size);
        versions.push_back(base);
    }

    void addVersion(int root_position, int change_index, T new_value) 
    {
        if (root_position < 0 || root_position >= versions.size() ||
            change_index < 0 || change_index >= versions[root_position].size()) 
        {
            std::cout << "Invalid root position or change index!" << std::endl;
            return;
        }
        
        auto temp = versions[root_position]; 
        undo_stack.push({root_position, change_index});
        temp[change_index] = new_value;     
        versions.push_back(temp);
        while (!redo_stack.empty()) 
        {
            redo_stack.pop();  // Очистим redo-стек после нового изменения
        }
    }
    
    void undo() 
    {
        if (undo_stack.empty()) 
        {
            std::cout << "No actions to undo!" << std::endl;
            return;
        }
        
        //auto [root_position, change_index] = undo_stack.top();
        auto top_item = undo_stack.top();
        auto root_position = top_item.first;
        auto change_index = top_item.second;
        
        undo_stack.pop();
        
        if (root_position == 0) 
        {
            std::cout << "Cannot undo the base version!" << std::endl;
            return;
        }

        redo_stack.push({root_position, change_index});  // Поместить в redo-стек

        int previous_version = root_position - 1; // Вернуться к предыдущей версии
        if (previous_version >= 0) 
        {
            versions.push_back(versions[previous_version]); // Копируем предыдущую версию
        }
    }
    
    void redo() 
    {
        if (redo_stack.empty()) 
        {
            std::cout << "No actions to redo!" << std::endl;
            return;
        }

        //auto [root_position, change_index] = redo_stack.top();
        auto top_item = redo_stack.top();
        auto root_position = top_item.first;
        auto change_index = top_item.second;
        
        redo_stack.pop();

        // При повторе мы просто используем последнюю версию
        addVersion(root_position, change_index, versions[root_position][change_index]); // Можно настроить обработку со значением
    }
    
    void printAllVersions() 
    {
        for (size_t i = 0; i < versions.size(); i++) 
        {
            std::cout << "Version [" << i << "]: \t{";
            for (size_t j = 0; j < versions[i].size(); j++) 
            {
                std::cout << "'" << versions[i][j] << "'";
                if (j < versions[i].size() - 1) 
                {
                    std::cout << ", ";
                }
            }
            std::cout << "}\n";
        }
    }
};