C++类能否通过模板实现所有数据成员的通用Getter/Setter?
C++通用Getter/Setter的模板实现方案
问题背景
你定义了包含多个protected成员的类:
class Obj { protected: cls1 class1; cls2 class2; // ... clsn classn; };
需要让外部访问这些成员,但不想为每个成员手动编写Getter/Setter,希望通过模板实现通用方案。
可行方案解析
1. 基于类型匹配的模板实现(适用于成员类型唯一的场景)
你的思路是可行的,通过std::is_same_v判断类型来匹配对应成员,结合std::enable_if_t实现模板重载:
#include <type_traits> class Obj { protected: cls1 class1; cls2 class2; public: // Getter:根据返回类型匹配成员 template<typename T> std::enable_if_t<std::is_same_v<T, cls1>, const T&> get_stats() const { return class1; } template<typename T> std::enable_if_t<std::is_same_v<T, cls2>, const T&> get_stats() const { return class2; } // Setter:同理根据参数类型匹配成员 template<typename T> std::enable_if_t<std::is_same_v<T, cls1>, void> set_stats(const T& val) { class1 = val; } template<typename T> std::enable_if_t<std::is_same_v<T, cls2>, void> set_stats(const T& val) { class2 = val; } };
缺点:如果存在多个同类型的成员(比如两个int类型成员),模板无法区分,会导致编译错误。
2. 基于标签结构体的模板实现(解决同类型成员冲突)
为每个成员定义唯一的标签结构体,通过特化模板来绑定标签与成员:
// 定义成员对应的标签 struct TagClass1 {}; struct TagClass2 {}; // ... 为每个成员添加对应标签 class Obj { protected: cls1 class1; cls2 class2; public: // 声明通用模板 template<typename Tag> const auto& get_stats() const; template<typename Tag> void set_stats(const auto& val); }; // 特化TagClass1对应的Getter template<> const cls1& Obj::get_stats<TagClass1>() const { return class1; } // 特化TagClass1对应的Setter template<> void Obj::set_stats<TagClass1>(const cls1& val) { class1 = val; } // 同理特化TagClass2的版本 template<> const cls2& Obj::get_stats<TagClass2>() const { return class2; } template<> void Obj::set_stats<TagClass2>(const cls2& val) { class2 = val; }
优点:完全避免同类型成员的冲突,每个成员通过唯一标签精准定位;缺点:需要为每个成员额外定义标签结构体,代码量略有增加。
3. 基于成员指针的通用模板实现(C++17及以上推荐)
利用C++17的非类型模板参数特性,直接将成员指针作为模板参数,无需额外特化或标签:
class Obj { protected: cls1 class1; cls2 class2; public: // Getter:通过成员指针直接访问 template<auto MemPtr> auto& get_stats() const { return this->*MemPtr; } // Setter:同理通过成员指针赋值 template<auto MemPtr> void set_stats(const auto& val) { this->*MemPtr = val; } }; // 使用示例 // Obj obj; // const cls1& c1 = obj.get_stats<&Obj::class1>(); // obj.set_stats<&Obj::class2>(cls2{});
优点:代码最简洁,无需额外定义标签或特化模板,天然支持同类型成员;注意:需要编译器支持C++17或更高版本。
方案选择建议
- 如果所有成员类型唯一,优先选基于类型匹配的方案,代码最直观;
- 如果存在同类型成员,优先选基于成员指针的方案(C++17+),兼顾简洁性与灵活性;
- 若编译器版本较低,可选用基于标签的方案。
内容的提问来源于stack exchange,提问作者Jack Lee
相关产品推荐
相关产品推荐

