关于顶层和底层的const的区别

关于顶层和底层的const的区别

举例说明

int i = 0;
int *const p1 = &i;              // p1变量本身是一个指针常量,不能被修改,所以这个const是一个顶层const   
const int j = 0;                     // j变量本身是一个整型常量,不能被修改,所以这个const也是一个顶层const   
const int *p2 = &j;              // p2可以被修改,所以这个const不是顶层const,*p2不能被修改,所以它是一个底层const   
const int *const p3 = p2;   // p3和*p3都是常量,那么右边的const是一个顶层const,左边的const则是一个低层const   
const int &r= j;                   // r是一个整型常量引用,所以这个const是一个底层const,即用于声明引用的const都是底层const   


那么执行对象的拷贝动作时,对底层const影响较大,对顶层const不受什么影响。

i = j;         // 正确拷贝了j的值,j是一个顶层const,个人觉得正确的原因是因为,如果j是个整型常量,赋值给另一个非常量是不会有安全问题的。   
p2 = p3;  // 正确拷贝,底层const p3赋值给了底层const p2,顶层const p3不受影响,原因同上,   
                //  但是我们反过来看底层const为什么也没问题呢,个人理解原因是*p2不可修改,这样的话即使p3赋值给了p2,也不会修改*p3,不会影响p3   


我们来看一些反例:

int *p = p3;  // 错误:直观上看p3有底层const,p什么也不是,所以不具备相同底层const的两个变量赋值是会有问题的   
                     // 但是从背后的影响角度上个看,我个人觉得是因为*p是可以修改的,但是*p3不可修改,明显的冲突了   
p2 = &i;      // 正确:p2是底层const,i什么也不是,官方解释int*能转成const int *,是可以赋值给p2的   
                    // 不过我建议还是从背后影响来看比较合理,任何对p2的操作影响i的值都是可以的   
int &r = j ;   // 错误,与底层const无关,所以说嘛,从深层次的角度看,j不可以修改,而通过r来修改j的值是不行的,所以非法   
const int &r2 = i;   // 正确,i只是个普通常量,但是由于r2是常量引用,可以被赋值,如果是非const则不可以   


其他情况

另外说到使用auto的时候,会忽略顶层const,同时底层const会留下来,例如:

int i = 0;
const int ci = i, &cr = ci;
auto a = ci;      // ci 是顶层const被忽略掉,所以auto a 等价于int a;
                         // 但其实我们来做个假设,如果保留了顶层const,那么auto a就等价于const int a看起来问题也不大,但是这样的话感觉缺失了灵活性,
                         // 因为int a更灵活,可以修改a的值,所以感觉为什么官方需要在auto a 加上const才表示要转成const,这样比较灵活一些。
auto b = &ci;  //ci的值不可以修改,是一个底层const,所以b也是底层const,等价于 const int *b