const在 C++ 里不是「加个修饰」那么简单——放在*左边还是右边,含义完全不同;放在成员函数末尾,改的甚至是隐藏的this指针类型。搞混这两者,轻则编译报错,重则写出自以为只读、其实能改的接口。这篇把const的位置语义、它对函数签名和性能的影响,用代码和真实输出讲清楚。
官方文档:cv 限定符(const/volatile)、成员函数
1. 引子:const int* 和 int* const 到底谁不能改
一句话区分:const紧贴谁,谁就不能改。
const int* p:const在*左边,修饰「指向的对象」——*p不能改,p能改(可指向别处)。int* const p:const在*右边,修饰「指针本身」——p不能改,*p能改。const int* const p:两边都有,p和*p都不能改。
2. 三种写法,逐个类型实证
用std::is_same把三种指针的精确类型钉出来:
// demo.cpp — 编译: g++ -std=c++17 -Wall -O2 demo.cpp -o demo#include<iostream>#include<type_traits>intmain(){intn=1;constintc=2;constint*p1=&c;// 指向 const int 的指针(底层 const)int*constp2=&n;// const 指针(顶层 const)constint*constp3=&c;// 顶层 + 底层 conststatic_assert(std::is_same<decltype(p1),constint*>::value,"");static_assert(std::is_same<decltype(p2),int*const>::value,"");static_assert(std::is_same<decltype(p3),constint*const>::value,"");std::cout<<"p1: const int* = "<<std::is_same<decltype(p1),constint*>::value<<'\n';std::cout<<"p2: int* const = "<<std::is_same<decltype(p2),int*const>::value<<'\n';std::cout<<"p3: const int* const = "<<std::is_same<decltype(p3),constint*const>::value<<'\n';}p1: const int* = 1 p2: int* const = 1 p3: const int* const = 1用一张图记住位置:
const int* p1; 底层 const:指针可变,所指不可变 ↑修饰所指对象 p1 = &x; ✓ *p1 = 0; ✗ int* const p2; 顶层 const:指针不可变,所指可变 ↑修饰指针本身 p2 = &x; ✗ *p2 = 0; ✓ const int* const p3; 两层都 const ↑所指 ↑指针3. 顶层 const(top-level)与底层 const(low-level)
这两个词是理解「为什么 const 影响重载、又不影响另一些重载」的关键:
- 顶层 const:修饰「对象自己」(
int* const、const int x)。 - 底层 const:修饰「指针/引用指向的东西」(
const int*、const int&)。
关键规则:函数按值传参时,顶层 const 会被忽略,不构成重载;底层 const 不会忽略,构成重载。