如何检测成员是否由目标类自身声明而非继承所得?
如何检测类自身声明的成员(而非继承所得)
当然可以不借助反射机制实现这个需求!问题的核心是要区分类自身声明的成员和从基类继承来的成员,而std::experimental::is_detected会把继承的成员也判定为存在,所以我们需要更精巧的SFINAE技巧来做区分。
针对你的类型别名检测需求的解决方案
我们可以通过递归检查类的基类,判断目标成员类型是否来自继承而非自身声明。以下是修改后的代码,基于C++20的requires表达式实现通用检测:
#include <type_traits> #include <experimental/type_traits> struct base { using type = std::true_type; }; struct derived : public base { }; // 检测T是否自身声明了type类型别名 template <typename T, typename = void> struct has_own_type : std::false_type {}; template <typename T> struct has_own_type<T, std::void_t<typename T::type>> { private: struct tag {}; // 递归检查基类是否包含相同的type类型 template <typename B> static constexpr bool check(std::enable_if_t<std::is_base_of_v<B, T> && !std::is_same_v<B, T>, tag>*) { if constexpr (requires { typename B::type; }) { // 如果基类B的type和T的type相同,说明是继承来的;否则继续递归检查B的基类 return std::is_same_v<typename B::type, typename T::type> || check<B>(nullptr); } else { // 基类B没有type,终止递归 return false; } } // 终止条件:没有匹配的基类 template <typename B> static constexpr bool check(...) { return false; } public: // 如果没有任何基类包含相同的type,说明是自身声明的 static constexpr bool value = !check<T>(nullptr); }; template<typename T> constexpr inline bool has_type_v = has_own_type<T>::value; int main () { static_assert (has_type_v<base>); // 成立:base自身声明了type static_assert (!has_type_v<derived>); // 成立:derived的type来自继承 }
工作原理
- 首先,
has_own_type模板先通过std::void_t检查T是否存在type类型别名。 - 如果存在,进入递归检查逻辑:
- 尝试将T转换为它的任意基类B(排除自身)。
- 检查基类B是否也存在
type,并且B的type与T的type是否相同。 - 如果找到这样的基类,说明T的
type是继承来的,返回false;否则返回true。
扩展到成员变量/函数
如果要检测成员变量或成员函数是否为自身声明,可以使用另一种基于名字隐藏的技巧:
- 创建一个包含同名成员的dummy类,让测试类和dummy类多重继承。
- 通过检测成员指针的类型,判断测试类是否自身声明了该成员(如果测试类有自身成员,多重继承后的成员指针会指向测试类的成员,而非dummy类的)。
比如检测成员函数void func()是否为自身声明:
template <typename T> struct has_own_func { private: struct dummy { void func(); }; struct derived : T, dummy {}; template <typename U, U> struct check; // 如果derived::func指向dummy::func,说明T没有自身的func template <typename U> static std::false_type test(check<void (dummy::*)(), &U::func>*); // 否则说明T有自身的func template <typename U> static std::true_type test(...); public: static constexpr bool value = decltype(test<derived>(nullptr))::value; };
总结
不需要依赖反射就能实现类自身成员的检测,核心是利用SFINAE、递归基类检查或名字隐藏的C特性。上述方案都是标准C实现,不需要编译器扩展。
内容的提问来源于stack exchange,提问作者levzettelin
相关产品推荐
相关产品推荐

