std::add_pointer是编译期类型转换工具,将t变为t*,不创建运行时指针;它不剥离引用或cv限定符,对int&等非法类型直接编译失败,常与decltype、remove_reference_t等配合用于泛型编程。

std::add_pointer 是个编译期工具,不是运行时指针创建函数
它不生成实际指针变量,只在类型层面把 T 变成 T*。常见误解是以为它能“给值加指针”,比如对 int x = 42; 调用 std::add_pointer<decltype>::type</decltype> 得到一个指向 x 的指针——其实它只产出类型 int*,你还得自己写 &x 才能得到值。
典型错误现象:std::add_pointer<int>::type p = 10;</int> 编译失败,因为 int* 不能直接赋整数;正确用法是 std::add_pointer<int>::type p = &x;</int> 或配合 decltype 推导已有变量的指针类型。
-
std::add_pointer<void>::type</void>展开为void*(合法) -
std::add_pointer<int>::type</int>展开为int*&(注意:是“指向 int 引用的指针”,C++ 不允许,编译报错) - 对
const T使用:std::add_pointer<const int>::type</const>→const int*(顶层 const,非int* const)
std::add_pointer 在模板推导中常和 decltype 搭配使用
真正实用场景是泛型代码里,你想让函数返回“某个表达式的指针类型”,但又不想硬写 int* 或 double* ——这时靠 decltype 抓类型,再用 std::add_pointer 包一层。
例如写一个通用取地址包装器:
立即学习“C++免费学习笔记(深入)”;
template<typename T>
auto make_ptr(T&& t) -> typename std::add_pointer<decltype(t)>::type {
return &t;
}注意:这里必须用 typename std::add_pointer<...>::type</...>,因为 std::add_pointer<t></t> 是依赖名称(dependent name),编译器需要 typename 告知后面是个类型。
- 如果传入右值(如
make_ptr(42)),decltype(42)是int,结果是int*,但取地址非法 —— 这说明你得加std::is_lvalue_reference约束或改用std::addressof - C++14 起可用别名模板简化:
std::add_pointer_t<t></t>替代typename std::add_pointer<t>::type</t>
std::add_pointer 对 cv 限定符和引用的处理容易出错
它的行为严格按标准定义:仅对“可加指针”的类型生效;否则 SFINAE 失败或编译错误。关键陷阱在于它不剥离引用或 cv 限定符 —— 它只是原样套上 *。
-
std::add_pointer_t<int></int>→ 编译错误(C++ 禁止int&*) -
std::add_pointer_t<const int></const>→ 同样错误(const int&*不合法) -
std::add_pointer_t<volatile int></volatile>→volatile int*(合法) - 想安全处理任意类型?先用
std::remove_reference_t和std::remove_cv_t预处理:std::add_pointer_t<:remove_cv_t>>></:remove_cv_t>
替代方案:多数时候直接用 auto 或类型别名更清晰
除非你在写 trait、type-erased 容器或深度元编程库,否则 std::add_pointer 很少是唯一解。现代 C++ 中,用 auto* 或 decltype(&x) 更直白,也避免模板嵌套带来的可读性问题。
例如:
int x = 42; auto p1 = &x; // p1 是 int* auto p2 = std::addressof(x); // 同样是 int*,且安全处理字面量/重载 operator&
-
std::add_pointer主要价值在类型计算管道中(比如配合std::decay构造统一指针接口) - 在 constexpr context 里它不可用(因为它不产生值),别指望用它做编译期指针算术
- 跨平台兼容性没问题,它是 C++11 标准组件,所有主流 STL 都实现
真正难的是判断什么时候该用它——而不是怎么用。它不解决“怎么取地址”,只解决“怎么描述‘那个地址的类型’”。一旦开始纠结 int&* 为什么错,就该回头检查是否真需要这层抽象。










