顶层const指指针本身不可改,底层const指指针所指对象不可改;关键看const紧邻位置:近类型为底层,近变量名为顶层。

const 修饰指针时,怎么一眼看出是顶层还是底层 const
顶层 const 指的是「对象本身不可改」,底层 const 指的是「它指向的东西不可改」。关键看 const 紧挨着谁:紧挨类型(如 int)就是底层;紧挨变量名(或指针符号 * 右侧)就是顶层。
常见错误现象:const int* p1 和 int const* p2 等价(底层 const),而 int* const p3 是顶层 const —— 但很多人读反,以为 const 在前就“管前面”,其实 C++ 从右往左读声明更可靠。
-
const int* p:p 可变,*p 不可变(底层 const) -
int* const p:p 不可变,*p 可变(顶层 const) -
const int* const p:p 和 *p 都不可变(两者都有)
为什么传参时底层 const 更常被要求
因为函数接口需要保证不修改实参所指内容,尤其在接收字面量、临时对象或只读缓冲区时。编译器会拒绝把 const char* 传给期望 char* 的函数,但反过来可以(隐式降级),这是底层 const 的核心约束力。
使用场景:字符串处理函数(如 strlen)、容器迭代器(std::vector<T>::cbegin() 返回 const_iterator)、自定义类的 getter 方法返回 const T&。
立即学习“C++免费学习笔记(深入)”;
- 传
const std::string&而不是std::string&,避免误改原对象 - 返回
const int&时,调用方不能通过该引用赋值,但能读取 —— 这是安全暴露内部状态的方式 - 若参数是
T* const(顶层 const 指针),其实没太大意义:指针本身在函数栈上,改不改都不影响调用方
auto 推导 const 时,顶层 const 被自动忽略
auto 声明变量时,顶层 const 会被丢弃,底层 const 会保留。这容易让人误以为“const 没了”,其实是语义设计:auto 关注的是初始化表达式的值类别和所指类型,而非绑定方式。
性能影响:无直接开销,但可能引发意外的拷贝或权限放宽。
-
const int ci = 42; auto x = ci;→x是int,不是const int -
const int* p = &ci; auto y = p;→y是const int*(底层 const 保留) - 要保留顶层 const,必须显式写
const auto z = ci;
constexpr 函数里 const 的作用和陷阱
constexpr 函数体内允许定义 const 变量,但它们不是编译期常量(除非也用 constexpr 声明)。这里 const 只表示运行时不可变,不影响是否能用于常量表达式。
容易踩的坑:以为 const int x = 5; 在 constexpr 函数里就能当编译期值用 —— 实际上只有 constexpr int x = 5; 才行。
-
constexpr int f() { const int a = 10; return a + 1; }✅ 合法,但a不是常量表达式 -
constexpr int g() { constexpr int b = 20; return b * 2; }✅b是编译期常量 -
const在constexpr函数中主要起文档和防御性作用,不改变求值时机
真正难的不是记清哪层 const,而是当 const 出现在模板参数、成员函数声明(void foo() const)、或者与 mutable 共存时,它的语义会叠加变化。这时候得回到“这个 const 禁止谁改什么”去逐层拆解,而不是套规则。










