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

C++包装类中模板代理插件功能实现报错求解

问题原因

编译报错来自两个模板设计问题:

  • Wrapper内部实例化代理类时,传入的Wrapper<Retriever, Updater, OwningClass>是无效类型:Wrapper本身需要4个模板参数,此处仅传3个,构成递归不完整类型引用,编译器无法推导该类型的内部成员(如GetterReturnType),直接导致std::invoke_result_t特化失败。
  • 原设计将第四个模板参数定义为模板模板参数(仅接受template<typename> class类型的参数),扩展灵活性差,后续代理如果需要额外模板参数就无法适配。
修复实现

去掉模板模板参数设计,将代理类型改为普通类型参数,通过前置声明+条件类型选择+CTAD推导指引保留原有使用习惯,完整可运行代码如下:

#include <cstdio>
#include <type_traits>
#include <utility>

using namespace std;

class testclass {
public:
  double get() { return d_; }
  void set(double d) { d_ = d; }
  double d_ = 0.0;
};

// 前置声明Wrapper模板
template <typename Retriever, typename Updater, typename OwningClass, typename Proxy = void>
struct Wrapper;

template <typename PropertyType>
struct DEFAULT_WRAPPER_PROXY {
  DEFAULT_WRAPPER_PROXY(PropertyType* p) : property_(p) {}

  operator typename PropertyType::GetterReturnType() {
    return property_->Get();
  }

  DEFAULT_WRAPPER_PROXY& operator=(typename PropertyType::GetterReturnType val) {
    property_->Set(val);
    return *this;
  }

  PropertyType* property_;
};

template <typename PropertyType>
struct LOGGING_WRAPPER_PROXY {
  LOGGING_WRAPPER_PROXY(PropertyType* p) : property_(p) {}

  operator typename PropertyType::GetterReturnType() {
    printf("[LOG] 执行属性读取操作\n");
    return property_->Get();
  }

  LOGGING_WRAPPER_PROXY& operator=(typename PropertyType::GetterReturnType val) {
    printf("[LOG] 执行属性写入操作,写入值:%f\n", val);
    property_->Set(val);
    return *this;
  }

  PropertyType* property_;
};

template<typename Retriever, typename Updater, typename OwningClass, typename Proxy>
struct Wrapper {
  using Self = Wrapper<Retriever, Updater, OwningClass, Proxy>;
  using ActualProxy = conditional_t<
    is_void_v<Proxy>,
    DEFAULT_WRAPPER_PROXY<Self>,
    Proxy
  >;

  Wrapper(Retriever retriever, Updater updater, OwningClass* owner) : retriever_(retriever), updater_(updater), containingClass_(owner) {}

  using GetterReturnType = invoke_result_t<Retriever, OwningClass>;
  GetterReturnType Get() { return (containingClass_->*retriever_)(); }

  template<typename...Args>
  using SetterReturnType = invoke_result_t<Updater, OwningClass, Args...>;

  template<typename...Args>
  SetterReturnType<Args...> Set(Args&&... args) { return (containingClass_->*updater_)((forward<Args>(args))...); }

  ActualProxy operator*() {
    return ActualProxy(this);
  }

  Retriever retriever_;
  Updater updater_;
  OwningClass* containingClass_;
};

// CTAD推导指引,无自定义代理时使用默认实现
template<typename Retriever, typename Updater, typename OwningClass>
Wrapper(Retriever, Updater, OwningClass*) -> Wrapper<Retriever, Updater, OwningClass, void>;

int main() {
  testclass tc;

  {
    // 默认代理场景,使用方式和原代码完全一致
    Wrapper pp(&testclass::get, &testclass::set, &tc);
    double y = *pp;
    *pp = 102;
    printf("默认代理场景属性值:%f\n", tc.d_);
  }

  {
    // 日志代理场景
    using BaseWrapper = Wrapper<decltype(&testclass::get), decltype(&testclass::set), testclass>;
    Wrapper<decltype(&testclass::get), decltype(&testclass::set), testclass, LOGGING_WRAPPER_PROXY<BaseWrapper>> pp2(&testclass::get, &testclass::set, &tc);
    double val = *pp2;
    *pp2 = 3.14159;
    printf("日志代理场景属性值:%f\n", tc.d_);
  }

  return 0;
}
设计说明
  • 彻底解决了原代码的递归类型引用问题,编译无报错。
  • 插件扩展无侵入:后续新增校验、缓存、权限控制等代理类,只要实现和现有代理一致的接口,直接作为第四个模板参数传入即可,不需要修改Wrapper核心逻辑。
  • 保留原使用习惯:不传自定义代理时支持类模板参数推导,和原有代码写法完全兼容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 02:39:09