std::is_pointer 是编译期类型 trait,返回 std::integral_constant 而非 bool,需配合 if constexpr、sfinae 或模板特化使用;对 cv 限定指针敏感,但不识别引用或未 decay 的类型,应先用 std::decay_t 归一化;支持所有指针类型(含 void*、函数指针、成员指针),区分需组合其他 trait。

std::is_pointer 的本质是类型 trait,不是运行时判断
它只在编译期起作用,返回的是 std::integral_constant<bool value></bool> 类型,不是 bool 值本身。直接用 if (std::is_pointer<t>::value)</t> 是错的——这会触发运行时分支,而 std::is_pointer<t>::value</t> 早在模板实例化时就确定了。
真正该做的是利用它的静态布尔值参与 SFINAE 或 if constexpr 分支:
- 在 C++17+ 中,优先用
if constexpr (std::is_pointer_v<t>)</t>(std::is_pointer_v是std::is_pointer<t>::value</t>的便捷别名) - 若需偏特化或重载,应定义模板特化版本,而不是在函数体内写 if 判断
-
std::is_pointer对 cv 限定符敏感:const int*、volatile char*、int* const都返回true;但int const*(等价于const int*)也是true,而int* const是指针类型,不是“指向 const 的指针”,这点常被误读
std::is_pointer 识别不了 decay 后的指针类型
它只对原始类型有效,对经过 std::decay 或引用折叠后的类型不自动生效。比如 T&&、T&、const T& 传入后,std::is_pointer<t>::value</t> 仍为 false,哪怕 T 本身是指针类型。
常见误用场景:写一个通用函数模板,想对“传入的是指针”做特殊处理,但没先去除引用和 cv 限定:
template<typename t>
void foo(T&& x) {
// ❌ 错误:x 是左值引用或右值引用,T 可能是 int*&&,std::is_pointer_v<t> 为 false
if constexpr (std::is_pointer_v<t>) { ... }
}</t></t></typename>
正确做法是先用 std::remove_reference_t 和 std::remove_cv_t(或直接 std::decay_t<t></t>)归一化:
std::is_pointer_v<:remove_reference_t>>></:remove_reference_t>- 更简洁:
std::is_pointer_v<:decay_t>></:decay_t>——但注意:std::decay_t<int></int>是int*,这时也会被识别为指针,要小心数组退化场景
std::is_pointer 对 void*、函数指针、成员指针都返回 true
它不区分指针种类:普通对象指针、void*、函数指针(如 int(*)())、甚至数据成员指针(int A::*)和成员函数指针(int (A::*)())——全部返回 true。
如果你需要进一步区分,不能只靠 std::is_pointer:
- 判函数指针:组合
std::is_function_v<:remove_pointer_t>></:remove_pointer_t> - 判成员指针:用
std::is_member_object_pointer_v<t></t>或std::is_member_function_pointer_v<t></t> -
void*本身不是void的指针(void是不完整类型),但标准特化了std::is_pointer_v<void></void>为true,这是例外
模板参数推导中 std::is_pointer_v 失效的典型场景
当模板参数是 auto 或未显式指定类型时,编译器推导出的 T 往往带引用,导致 std::is_pointer_v<t></t> 为 false,即使实参是指针。
例如:
template<typename t>
void bar(T x) {
static_assert(std::is_pointer_v<t>, "T must be pointer");
}
bar(new int(42)); // ❌ 编译失败:T 被推导为 int*,但 new 返回 int*,x 是值参数 → T = int*,其实这里能过;但若写成 bar(&x),x 是 int&,T = int*,也 OK。真正坑在下面:</t></typename>
更隐蔽的问题:
- 使用
auto推导 lambda 参数:[](auto p) { static_assert(std::is_pointer_v<decltype>); }</decltype>→p类型是值,但若传入nullptr,decltype(p)是std::nullptr_t,不是指针 - 转发引用场景下,
T是int* &&,std::is_pointer_v<t></t>是false,必须先std::remove_reference_t<t></t> - 模板别名里直接用
std::is_pointer_v<t></t>作条件,但T可能是未实例化的依赖类型(如类模板内),此时必须加typename和template关键字,否则报错
最稳妥的做法始终是:先标准化类型(去引用、去 cv),再查 trait,且确保在编译期上下文中使用(如 if constexpr、static_assert、SFINAE 条件)。别指望它在运行时分支里“猜出”指针意图——它根本不是为那个设计的。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











