std::is_pointer仅对裸指针类型(如int*)返回true,对智能指针、数组指针、成员指针均返回false;它只判断语法结构而非语义,需配合std::remove_pointer安全提取所指类型。

std::is_pointer 的核心能力与常见误判场景
std::is_pointer 是一个编译期常量表达式(constexpr bool),它只对「裸指针类型」返回 true,比如 int*、const char*、void*。但它对 std::unique_ptr、std::shared_ptr、数组指针(如 int(*)[5])或成员指针(如 int (A::*))一律返回 false —— 这不是 bug,是标准定义如此。
容易踩的坑:有人用它判断“是否为智能指针”或“是否可解引用”,结果在模板中误判 std::vector<int>::iterator</int>(可能是指针,但不保证)或 T* 被别名隐藏(如 using ptr_t = int*)后失效。记住:std::is_pointer 只看类型语法结构,不看语义。
在模板中安全提取指针所指类型:配合 std::remove_pointer
单靠 std::is_pointer 只能回答“是不是”,真正要用它做分支逻辑(比如偏特化、if constexpr),必须搭配 std::remove_pointer 来剥离指针层级,否则无法进一步操作目标类型。
典型实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
if constexpr (std::is_pointer_v<t>)</t>做编译期分支,避免实例化非法代码(比如对非指针调用*t) - 用
typename std::remove_pointer_t<t></t>获取被指向类型,再用于后续 trait 判断(例如检查该类型是否 trivially copyable) - 注意
std::remove_pointer对非指针类型直接返回原类型,所以它本身是安全的,但组合使用时仍需先确认std::is_pointer_v<t></t>为真,否则语义可能偏离预期
示例:
template<typename T>
auto get_pointee_size() {
if constexpr (std::is_pointer_v<T>) {
return sizeof(typename std::remove_pointer_t<T>);
} else {
return sizeof(T);
}
}
为什么不能用它识别 std::shared_ptr 等智能指针
因为 std::shared_ptr<int> 是类模板特化,不是语言内置指针类型;它的解引用行为来自 operator* 重载,而非类型系统标记。C++ 标准库中没有通用 trait 能跨所有智能指针统一识别“指针语义”,这是有意为之的设计隔离。
若你真需要覆盖智能指针,得手动扩展:
- 写自定义 trait,例如
is_smart_pointer_v<T>,通过 SFINAE 或std::is_class_v+ 成员检测(如是否有get()和operator*) - 不要试图用
std::is_pointer替代——它连int**都只认最外层,更不会穿透包装器 - 尤其注意:某些 iterator 实现是原生指针,但标准不保证;依赖
std::is_pointer判断 iterator 类型会导致跨平台或 STL 版本迁移失败
实际工程中更可靠的替代思路
单纯依赖 std::is_pointer 容易陷入“类型表象 vs 行为意图”的混淆。真实项目里,多数需求其实不是“它是不是指针”,而是“它能不能像指针一样用”或“它是否拥有资源所有权”。
推荐做法:
- 用
std::is_convertible_v<decltype(std::declval<T>().get()), void*>粗略试探是否有get()并能转成原始指针(适用于多数智能指针和自定义句柄) - 对所有权语义,优先检查
std::is_move_constructible_v<T>和析构行为,而非类型名 - 若需统一处理,定义概念(C++20
concept)比 trait 更清晰,例如concept pointer_like = requires(T t) { *t; t.get(); };
最常被忽略的一点:编译期识别只是起点,真正难的是后续路径的泛化实现——比如对 int* 直接取地址,对 std::unique_ptr<int> 调 .get(),二者接口不同,必须分治,不能幻想一个 trait 解决所有问题。


















