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
相关产品推荐
相关产品推荐

