C++ 跟踪 class 中的更改

C++ Keep tracks of changes inside a class

想象一个 class 代表一封邮件 :

class mail {
    string subject;
    string content;
    date receivedDate;
};

现在我想要实现的是知道我的邮件数据是否已设置,一旦设置,哪些发生了变化。我可以像这样组合 std::optional 和 std::map :

class Mail {
    std::optional<string> subject;
    std::optional<string> content;
    std::optional<date>   receivedDate;

    enum EField { Subject, Content, ReceivedDate };

    typedef std::map<EField, bool> ChangedMap;

    ChangedMap changedFields;

public:
    Mail(optional<string> subject, ... ) {
        // initialize map with fields... hard coded
    }

    bool HasSubject() const { return subject; }

    string GetSubject() const { return subject.get(); }

    void SetSubject(const std::string& newSubject) {
        subject = newSubject;
        changedFields[Subject] = true;
    }

    void RemoveSubject() {
        changedFields[Subject] = HasSubject();
        subject.reset();
    }

    bool IsSubjectChanged() const {
        return changedFields[Subject];
    }
};

但我真的认为我在这里遗漏了一些重要的东西。您能找到更好的方法吗,最好是使用更少的内存并且没有硬编码值?

我考虑过从 std::optional 继承,但我也不认为这是一件好事。

谢谢

让我们概括一下这个问题:给定一个类型 T,我想要一个包装器 tracked<T> 来跟踪 reads/writes 在 [=32= 的历史记录].

我会通过使用 std::tuple 和元编程来解决这个问题。首先,让我们根据 std::tuple:

来定义 mail
class mail
{
private:
    std::tuple<string, string, date> _data;

public:
    // `variant_type` could be automatically computed from the
    // tuple type.
    using variant_type = std::variant<string, string, date>; 

    enum class index
    {
        subject = 0,
        content = 1,
        date = 2
    };

    template <index TIndex>
    decltype(auto) access()
    {
        return std::get<static_cast<std::size_t>(TIndex)>(_data);
    } 
};

然后我会创建类似 tracked<T> 的东西来跟踪在 T 上执行的操作:

template <typename T>
class tracked
{
private:
    using index_type = typename T::index;
    using variant_type = typename T::variant_type;

    struct write_action
    {
        variant_type _before;
        variant_type _after;
    };

    struct read_action
    {
         index_type _index;
    };

    T _data;
    std::vector<std::variant<write_action, read_action>> _history;

public:
    template <index TIndex>
    const auto& read() const 
    {
        _history.emplace_back(read_action{TIndex});
        return _data.access<TIndex>();
    }

    template <index TIndex, typename T>
    void write(T&& new_value) const 
    {
        // Remember previous value.
        variant_type _before{_data.access<TIndex>()};

        _history.emplace_back(write_action{_before, new_value});
        return _data.access<TIndex>() = std::forward<T>(new_value);
    }
};

上面的代码并不完全正确,因为您需要 action 类型的构造函数、异常处理、移动语义支持等等。不过,我希望您能大致了解一下。