C++中定义非静态成员时获取this对应类类型的方法探究
这个问题确实挺棘手的——在类内部定义非静态成员时,想要不写类名就能拿到当前类的类型,C++的语法规则确实给我们设了不少限制。让我们一步步拆解你的尝试,再聊聊可行的解决办法:
为什么你的几种尝试会失败?
1. 类模板实参推导(CTAD)的限制
你尝试用inner bar{this};让编译器自动推导模板参数,但触发了错误:
error: use of class template 'inner' requires template arguments; argument deduction not allowed in non-static struct member
inner bar{this};
这是因为C标准(包括C17及之后)明确规定:类模板实参推导不能用于非静态数据成员的声明。在类成员的声明阶段,类本身还处于不完全定义状态,编译器无法在这里触发CTAD逻辑。
2. decltype(*this)的使用限制
你尝试用inner<decltype(*this)> bar{this};获取当前类型,但得到错误:
error: invalid use of 'this' outside of a non-static member function
inner<decltype(*this)> bar{this};
this指针的使用范围被严格限定在非静态成员函数的函数体内部,哪怕是数据成员的就地初始化表达式中,也不能将this用于模板实参的推导(因为此时类还未完全定义,this的类型是不完全的)。
3. 直接写类名的局限性
你最初的可行代码inner<foo> bar{this};能在GCC和Clang编译通过,是因为在类成员声明中,类名可以被提前引用(编译器会将其视为不完全类型,只要模板inner的实例化在类完全定义后完成即可)。但这个方案对你来说不可行,因为你需要把inner放入宏中,无法显式写出类名。
最优解决方案:奇异递归模板模式(CRTP)
你采用的CRTP方案是目前兼容性最强、最通用的解决办法,支持所有C++版本(从C++98开始即可使用):
template<typename T> struct SelfAwareType { using myOwnType = T; }; struct foo : SelfAwareType<foo> { inner<myOwnType> bar{this}; };
这个方案的核心思路是:
- 定义一个模板基类
SelfAwareType,它接收派生类的类型作为模板参数,并通过类型别名myOwnType暴露这个类型。 - 派生类继承自
SelfAwareType<派生类>,之后在派生类内部就可以用myOwnType指代自身的类型,完全不需要显式写出类名——完美适配你的宏需求。
这个方法不仅兼容性拉满,而且代码清晰易懂,是这类场景下的标准解决方案。
内容的提问来源于stack exchange,提问作者Dror Speiser

