T240516 Top-level const and Low-level const
本篇分享详细解释一下 Top-level const 和 Low-level const 的概念。
Top-level 和 Low-level 指的是名称修饰符所处的位置,const 是最常见的修饰符,举一反三,便可知所有情况。
由例子切入理解:
int a = 42;
int b = 10;
int* const p1 = &a; // top-level const
*p1 = 22; // Ok
p1 = &b; // Error!
int const* p2 = &a; // low-level const
*p2 = 22; // Error!
p2 = &b; // Ok
p1
的类型为 a constant pointer to int
,p2
的类型为 a pointer to constant int
。Top 和 Low 表示修饰符距实际类型的位置,前者修饰指针本身,后者修饰类型本身。因此,p1
可以改变 a
的值,但无法对指针再次绑定,p2
则完全相反。
大多数类型都支持 Top-level const,而 Low-level const 常出现于指针和引用类型。
首先应该着重强调的是引用类型。引用类型是个可以自动解引用的只读指针类型,下面两种是等价写法:
int a = 42;
int b = 10;
// reference type
int& ri = a;
ri = 22;
// equivalent with reference
int* const pi = &a;
*pi = 22;
Modern C++ 风格的代码几乎很少需要显式地使用指针类型,引用类型使用起来要更加简便和安全。
引用类型相当于自动添加了 Top-level const,可以手动再添加 Low-level const。指针类型则是相当特殊的一类,它可以同时添加二者。
// pi has both top-level const and low-level const
int const * const pi = &a;
它们之间最大的不同在于,Top-level const 可能会被忽略,而 Low-level const 永远不会被忽略。
第一种情境是对象拷贝。
int a = 42;
int const* const pi = &a;
int const* pc = pi; // ok, top-level const in pi is ignored
int* p = pi; // error, low-level const in pi cannot be ignored
int const b = 10; // top-level const
int& r = b; // error, cannot bind a ordinary int& to a const int object
int const& rc = c; // ok
第二种情境是模板参数推导。
template <class T>
void f(T t) {}
int main() {
int const i = 1; // top-level const
// T deduced as int, f<int>(int)
// top-level const is ignored.
f(i);
}
第三种情境是 auto 类型推导。
int const b = 0; // top-level const is ignored.
auto c = b; // c is an int
static_assert(std::is_same_v<decltype(c), int>);