检测类指针(可解引用)类型的模板函数对实际指针类型失效
解决自定义类指针检测对原生指针失效的问题
听起来你遇到的问题很典型——自己写的类指针检测机制,能处理非模板类型、std系列智能指针,但偏偏对原生指针(比如int*)失效了对吧?我来帮你捋清楚问题根源,再给出具体的修复方案。
问题根源分析
原生指针(比如int*、char*)的解引用操作(*和->)是C++的内置语法,并不是类的成员函数。而你之前的检测逻辑,大概率只针对类类型的operator*()和operator->()成员函数做了判断,完全没覆盖到原生指针这种非类类型的情况。这就导致你的特化和检测逻辑对原生指针直接“视而不见”。
修复方案:覆盖原生指针+通用检测逻辑
我们可以调整检测模板,同时支持两种情况:类类型的成员解引用运算符,以及原生指针的内置解引用操作。这里用C17的std::void_t来做SFINAE检测(如果是C11/14,可以自己实现一个简单的void_t)。
1. 实现基础检测模板
首先,我们先定义用于检测operator*()的模板:
#include <type_traits> // 自定义void_t(C++11/14需要,C++17可直接用std::void_t) template<typename...> using void_t = void; // 默认情况:不可解引用 template<typename T, typename = void> struct is_pointer_like_dereferencable : std::false_type {}; // 通用检测:只要*obj是合法操作(不管是内置还是成员operator*) template<typename T> struct is_pointer_like_dereferencable<T, void_t<decltype(*std::declval<T>())>> : std::true_type {};
然后是检测operator->()的模板,这里需要注意:原生指针没有operator->()成员函数,所以我们要单独给原生指针做特化,同时检测类类型的成员operator->():
// 默认情况:不可通过->解引用 template<typename T, typename = void> struct is_pointer_like_arrow_dereferencable : std::false_type {}; // 检测类类型的operator->()成员 template<typename T> struct is_pointer_like_arrow_dereferencable<T, void_t<decltype(std::declval<T>().operator->())>> : std::true_type {}; // 特化原生指针:原生指针支持->操作 template<typename T> struct is_pointer_like_arrow_dereferencable<T*> : std::true_type {};
2. 组合两个检测结果
最后,is_pointer_like直接结合前两个检测的结果:
template<typename T> struct is_pointer_like : std::conjunction< is_pointer_like_dereferencable<T>, is_pointer_like_arrow_dereferencable<T> > {}; // 方便使用的变量模板 template<typename T> constexpr bool is_pointer_like_v = is_pointer_like<T>::value;
3. 测试验证
你可以用这些静态断言来验证效果:
// 原生指针应该通过检测 static_assert(is_pointer_like_v<int*> == true, "int* should be recognized as pointer-like"); // std智能指针应该通过检测 static_assert(is_pointer_like_v<std::unique_ptr<int>> == true, "unique_ptr should be pointer-like"); static_assert(is_pointer_like_v<std::shared_ptr<int>> == true, "shared_ptr should be pointer-like"); // 普通类型不应该通过检测 static_assert(is_pointer_like_v<int> == false, "int should NOT be pointer-like"); static_assert(is_pointer_like_v<std::string> == false, "string should NOT be pointer-like");
额外说明
如果你之前的特化是针对std系列模板(比如std::unique_ptr、std::shared_ptr),其实现在这个通用逻辑已经能覆盖它们了,不需要再单独写特化——因为这些智能指针都实现了operator*()和operator->()成员函数,会被SFINAE条件检测到。
内容的提问来源于stack exchange,提问作者Timo
相关产品推荐
相关产品推荐

