C++私有静态const成员检测异常:派生类误判拥有基类私有成员
问题描述
我试图构建一个成员检测 trait,通过私有成员isA区分基类与派生类。参考相关实现编写了如下C++代码:
// main.cpp #include <iostream> template<typename T, typename...> using first_type = T; class private_const_test { template<typename = void> static constexpr std::false_type has_const_test_impl(...) { return std::false_type(); } template<typename T> static constexpr first_type<std::true_type, decltype(T::isA)> has_const_test_impl(T) { return std::true_type(); } public: template<typename T> static constexpr bool has_const_test = decltype(has_const_test_impl(std::declval<T>()))::value; // public: // template <typename T> // using has_DoGetDataChain_t = decltype(T::isA); // template <typename T> // static constexpr bool has_const_test = // std::experimental::is_detected_v<has_DoGetDataChain_t, T>; }; template<typename T> constexpr bool has_private_const_test = private_const_test::has_const_test<T>; class A { private: friend class private_const_test; static constexpr bool isA = true; }; class B : public A { }; class C : public B { }; class D :public C { }; int main() { std::cout << std::boolalpha; std::cout << has_private_const_test<A> << std::endl; // true std::cout << has_private_const_test<B> << std::endl; // false std::cout << has_private_const_test<C> << std::endl; // false std::cout << has_private_const_test<D> << std::endl; // false }
预期输出为true false false false,但实际得到true true true true,该如何解决?
问题根源
问题出在成员查找规则上:T::isA的非限定名查找会遍历整个继承链。虽然isA是A的私有成员,但private_const_test是A的友元,因此它有权访问A的私有成员——哪怕是通过派生类B/C/D的类型去访问继承来的isA,编译器也会判定为合法。
解决方案
要让检测逻辑只识别类自身定义的isA(而非继承来的),需要利用成员指针的类型差异:继承来的静态成员指针,其类型会关联到基类而非派生类。修改后的代码如下:
#include <iostream> #include <type_traits> template<typename T, typename...> using first_type = T; class private_const_test { // 兜底重载:匹配无自身isA成员的类型 template<typename = void> static constexpr std::false_type has_const_test_impl(...) { return std::false_type(); } // 仅当T自身拥有isA静态成员时匹配此重载 template<typename T> static constexpr first_type<std::true_type, decltype(std::remove_cvref_t<T>::isA), // 验证成员指针类型是否属于T自身 std::enable_if_t<std::is_same_v<decltype(&std::remove_cvref_t<T>::isA), const bool*>>> has_const_test_impl(T) { return std::true_type(); } public: template<typename T> static constexpr bool has_const_test = decltype(has_const_test_impl(std::declval<T>()))::value; }; template<typename T> constexpr bool has_private_const_test = private_const_test::has_const_test<T>; class A { private: friend class private_const_test; static constexpr bool isA = true; }; class B : public A { }; class C : public B { }; class D : public C { }; int main() { std::cout << std::boolalpha; std::cout << has_private_const_test<A> << std::endl; // true std::cout << has_private_const_test<B> << std::endl; // false std::cout << has_private_const_test<C> << std::endl; // false std::cout << has_private_const_test<D> << std::endl; // false }
原理说明
std::remove_cvref_t<T>用于消除类型的引用和CV限定符,确保操作的是原始类类型。decltype(&std::remove_cvref_t<T>::isA):如果isA是T自身的成员,其类型为const bool*;如果是继承自A的成员,类型为const bool A::*。通过std::is_same_v可以区分这两种情况,从而排除继承来的成员。- 只有当
T自身定义了isA时,才会匹配到返回std::true_type的重载,派生类因无自身isA成员,会匹配兜底的std::false_type重载。
内容的提问来源于stack exchange,提问作者storyteller
相关产品推荐
相关产品推荐

