language/implicit_conversion 限定性类型转换

写在前面

内容见 implicit conversion

限定性转换指的是和 cv 属性有关的类型转换,它的发生必须满足一定的条件。

相似类型

忽略掉各层的 cv 属性,如果两个类型形式相同,就是相似类型。比如 const int* volatile *int** const

Important

函数指针对应函数的参数返回值类型的 cv 属性不可以忽略。可见原页面的例子,这里略去。

限定性分解

合并限定性和类型转换规则

规则说明

  • C++20 之前 T1T2类型转换规则限定性组合类型是分开定义的,而 C++20 之后类型转换规则是借由限定性组合类型定义的。
  • 限定性组合类型的两个参数类型是有顺序的,AB 的限定性组合类型、BA 的限定性组合类型是两码事。
  • C++20 中,限定性组合类型的定义改变了,但是类型转换的规则没有变:
    • 要求限定性组合类型是无 cv 限定的 T2,说明这个类型和 T2 相似,同时它又必须和 T1 相似(第一排),所以 C++20 之前 T1T2 相似的要求没有变。
    • cv3_icv1_icv2_i 的并集” 是对 “对于每个非零 i,如果 cv1_i 中有 const,……”的转述。
    • 限定性组合类型新增的对于 Pj_i 的描述不会影响类型转换的判断,因为 T1T2 必须相似,所以对于固定的 iPj_i($j=1, 2, 3$)必须相同。

C 和 C++ 的不同之处

char** p = 0;
char * const* p1 = p;       // C 与 C++ 中 OK
const char* const * p2 = p; // C 中错误,C++ 中 OK

Note

即便是在 C++ 中加上 extern "C",依然是用 C++ 的语法检查,而不会用 C 的语法检查。另外,在这个问题上,C 语言编译器虽然认为有错误,但是依然可能会继续编译,比如 gcc 只是会给出不兼容类型转换的警告。

简单地说明转换规则

定义 1:将靠近变量名的一侧称为内层,最内层、0 层都是指这一层,而向外层数逐渐增大。

定义 2:对于两个相似类型 T1T2T1T2 在某一层更严格指的是 T1T2 某一层 cv 限定符的基础上,增加了 const 或者 volatile 属性。

想要 T1 转换成 T2

  1. 两者必须相似。
  2. 忽略 0 层的 cv 限定符,T2 每一层的类型要相同严格、或者更严格。
  3. 从最外层起到第 1 层(包含),令 T2 第一个更严格的层为 j,需要 j 不存在、或者 [i, j) 中都有 const 属性(在 C++ 中) / j 为 1(在 C 中)。

代码例子

void f() {
    char ** const p = nullptr;
    char volatile * const *p1 = p;
    // char volatile **p2 = p;
    // 第 2 层有了 volatile,所以 [1, 2) 层(也就是第 1 层)需要加 const
}