如何在类内部无需显式类名获取成员指针?
在C++类内部定义static constexpr成员指针时省略类名的问题与解决方案
错误原因分析
你遇到的编译错误本质是静态成员初始化的上下文限制:
- 在类的
static constexpr成员初始化器中,直接使用&id时,编译器无法将其解析为Type::*类型的成员指针——静态成员不属于任何类实例,此时没有隐含的类作用域上下文,必须显式用类名限定(&Type::id)才能明确指向当前类的非静态成员。 - 第一个错误触发后,初始化器失效,编译器认为
constexpr成员被默认初始化,而const类型的成员指针不允许默认初始化,因此引发后续错误;auto推导也因初始化器无效而失败。
解决方案:用宏简化类名重复书写
由于C++标准目前不支持在静态成员初始化中隐式推导当前类的作用域,最实用的方案是通过宏自动插入类名限定,避免手动重复书写类名,同时满足模板库的易用性需求。
1. 基础宏实现(适用于普通类)
#include <iostream> // 宏:定义静态成员指针,自动添加类名限定 #define DEFINE_STATIC_MEMBER_PTR(CLASS_NAME, PTR_NAME, MEMBER_NAME) \ static constexpr auto PTR_NAME = &CLASS_NAME::MEMBER_NAME; struct Type { int id; // 只需传入类名、指针名、成员名 DEFINE_STATIC_MEMBER_PTR(Type, mem_ptr_id, id); DEFINE_STATIC_MEMBER_PTR(Type, mem_ptr_id_auto, id); }; int main() { Type test; test.*Type::mem_ptr_id = 5; test.*Type::mem_ptr_id_auto = 5; std::cout << test.id << std::endl; // 输出:5 return 0; }
2. 适配Result模板库的方案
针对你开发Result<...>模板库的需求,我们可以设计更易用的宏,结合模板的get<PROPERTY>方法:
#include <iostream> #include <string> // 模板库核心:提供get<PROPERTY>方法 template<typename... Properties> struct Result { // 支持左值、右值、const对象的get方法 template<auto MemPtr> decltype(auto) get() & { return this->*MemPtr; } template<auto MemPtr> decltype(auto) get() const& { return this->*MemPtr; } template<auto MemPtr> decltype(auto) get() && { return std::move(this->*MemPtr); } }; // 宏:在Result子类中添加属性及对应的静态成员指针 #define ADD_RESULT_PROPERTY(CLASS_NAME, PROP_TYPE, PROP_NAME) \ PROP_TYPE PROP_NAME; \ static constexpr auto PROP_NAME = &CLASS_NAME::PROP_NAME; // 测试用例:自定义Result子类 struct UserResult : Result<int, std::string> { ADD_RESULT_PROPERTY(UserResult, int, id); ADD_RESULT_PROPERTY(UserResult, std::string, name); }; int main() { UserResult res; // 语法1:直接访问成员 res.id = 101; res.name = "Bob"; // 语法2:使用get<PROPERTY>方法 std::cout << res.get<UserResult::id>() << std::endl; // 输出:101 std::cout << res.get<UserResult::name>() << std::endl; // 输出:Bob return 0; }
3. 进一步简化:避免重复传入类名(编译器扩展)
如果想彻底避免在宏中重复写类名,可以利用GCC/Clang的__typeof__扩展(非标准C++特性,跨平台需谨慎):
// 兼容GCC/Clang的宏,无需手动传入类名 #define AUTO_STATIC_MEMBER_PTR(PTR_NAME, MEMBER_NAME) \ static constexpr auto PTR_NAME = &__typeof__(*this)::MEMBER_NAME; struct Type { int id; AUTO_STATIC_MEMBER_PTR(mem_ptr_id, id); };
为什么自定义Pointer2Member包装类无效?
你尝试的自定义包装类仍报错,本质原因和直接写&id一致:包装类的初始化仍处于静态成员上下文,没有类实例,无法隐式推导当前类的作用域,必须显式指定&Type::id才能生成正确的成员指针。
内容的提问来源于stack exchange,提问作者Alex Tsvetanov
相关产品推荐
相关产品推荐

