You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Observed类?使用std::invoke重定向函数调用遇问题求助

解决Observed类的内部对象修改追踪问题

问题概述

设计Observed<T>类用于存储T类型对象,并通过m_updated标记追踪对象自上次读取后是否被修改。初始实现存在缺陷:当调用value()返回内部对象的引用后,直接修改对象(例如对std::vector<int>调用push_back)会绕过write()方法,导致m_updated无法被正确标记为true。尝试添加call()方法来包裹修改操作,但调用std::vector等带模板参数的类的成员函数指针时遇到困难。

解决方案

方案1:使用代理类自动追踪修改

不直接返回原始对象的引用,而是返回一个代理对象,所有通过代理对内部对象的操作都会自动触发m_updated标记。这种方式用法更自然,无需手动处理成员函数指针:

#include <concepts>
#include <utility>

template <std::copyable T>
class Observed
{
public:
    Observed() : m_data{}, m_updated{false} {};
    Observed(const T& data) : m_data{data}, m_updated{true} {};

    // 移动、拷贝构造/赋值函数(按需实现)
    Observed(Observed&&) = default;
    Observed& operator=(Observed&&) = default;
    Observed(const Observed&) = default;
    Observed& operator=(const Observed&) = default;

    // 返回代理对象,用于修改内部数据
    class ModifyProxy
    {
    public:
        ModifyProxy(T& data, bool& updated) : m_data(data), m_updated(updated) {}

        // 重载->运算符,指向内部对象并标记修改
        T* operator->()
        {
            m_updated = true;
            return &m_data;
        }

        // 重载*运算符,返回内部对象引用并标记修改
        T& operator*()
        {
            m_updated = true;
            return m_data;
        }

    private:
        T& m_data;
        bool& m_updated;
    };

    ModifyProxy modify()
    {
        return ModifyProxy{m_data, m_updated};
    }

    // 只读访问,不修改标记
    const T& value() const
    {
        return m_data;
    }

    // 读取后重置更新标记
    const T& read_and_reset()
    {
        m_updated = false;
        return m_data;
    }

    bool modified() const
    {
        return m_updated;
    }

    template <typename U>
    requires(std::same_as<T, std::remove_cvref_t<U>>)
    void write(U&& value)
    {
        m_data = std::forward<U>(value);
        m_updated = true;
    }

private:
    T m_data;
    bool m_updated;
};

使用示例:

#include <vector>
#include <cassert>

int main()
{
    Observed<std::vector<int>> v;
    v.modify()->push_back(1);
    assert(v.modified() == true);

    const auto& vec = v.read_and_reset();
    assert(vec.size() == 1);
    assert(v.modified() == false);

    v.write(std::vector<int>{2,3});
    assert(v.modified() == true);
}

方案2:修复call()函数实现

如果坚持使用call()方法包裹修改操作,需要修正函数的返回值类型推导,并正确处理成员函数指针的重载问题:

#include <concepts>
#include <utility>
#include <functional>

template <std::copyable T>
class Observed
{
public:
    Observed() : m_data{}, m_updated{false} {};
    Observed(const T& data) : m_data{data}, m_updated{true} {};

    // 移动、拷贝构造/赋值函数(按需实现)
    Observed(Observed&&) = default;
    Observed& operator=(Observed&&) = default;
    Observed(const Observed&) = default;
    Observed& operator=(const Observed&) = default;

    // 修正后的call函数:正确推导invoke的参数类型
    template<typename Func, typename... Ts>
    auto call(Func&& func, Ts&&... args) -> std::invoke_result_t<Func, T&, Ts...>
    {
        m_updated = true;
        return std::invoke(std::forward<Func>(func), m_data, std::forward<Ts>(args)...);
    }

    bool modified() const
    {
        return m_updated;
    }

    const T& value() const
    {
        return m_data;
    }

    const T& read_and_reset()
    {
        m_updated = false;
        return m_data;
    }

    template <typename U>
    requires(std::same_as<T, std::remove_cvref_t<U>>)
    void write(U&& value)
    {
        m_data = std::forward<U>(value);
        m_updated = true;
    }

private:
    T m_data;
    bool m_updated;
};

使用示例:
由于std::vector::push_back存在重载(const引用和右值版本),需要显式指定要调用的重载:

#include <vector>
#include <cassert>

int main()
{
    Observed<std::vector<int>> v;
    // 调用const引用版本的push_back
    v.call(static_cast<void (std::vector<int>::*)(const int&)>(&std::vector<int>::push_back), 1);
    assert(v.modified() == true);

    // 调用clear方法
    v.call(&std::vector<int>::clear);
    assert(v.value().empty());
}

方案对比

  • 代理类方案:用法直观,自动覆盖所有修改操作,无需关注成员函数的重载或模板参数问题,是更推荐的实现方式。
  • call()函数方案:适合需要显式控制修改操作的场景,但使用时需要手动处理成员函数指针的重载问题,相对繁琐。

内容的提问来源于stack exchange,提问作者Jonah F

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 14:10:35