typeid 运算符忽略 cv 限定符背后的理由是什么?

What is the rationale behind the typeid operator ignoring cv-qualifiers

本文关键字:背后 理由 是什么 运算符 cv typeid      更新时间:2023-10-16

摘自 ISO/IEC 14882:2003 的 C++ 语言标准 §5.2.8,引用,

始终忽略左值表达式的顶级 cv 限定符或作为 typeid 的操作数的类型 id。

做出这一决定的理由是什么。它在类型系统上打了一个洞,是违反直觉的。常量信息(或易失部分)在编译时是已知的,因此如果typeid只考虑cv限定符,则不会产生开销。

尽管能够重载具有constvolatileconst volatile变体的非静态成员函数(参见[class.mfct.nonstatic]),但类型Dconst Dvolatile Dconst volatile D(对于某些非CV限定的类型D)并非无关;该标准在[basic.type.qualifier]中指定,对于每个cv-nonqualible类型,每个cv-nonqualible类型。,则存在具有相同表示和对齐要求的类型的不同const合格、volatile合格和const volatile合格"版本"。如果Dconst D有可能彼此无关(例如,通过具有不同的表示,存储要求,或者在类的情况下,不同的成员),那么很多语言就会崩溃。

例如,类型D&的对象可隐式转换为const D&volatile D&const volatile D&(请参阅 [conv.qual])。但是,如果允许Dconst D无关,那么这种转换就没有意义了。

还要考虑标准中的许多地方指定忽略顶级 cv 限定符:

  • [over.load] 指定:

    仅在是否存在const和/或volatile时才不同的参数声明是等效的。也就是说,在确定要声明、定义或调用哪个函数时,将忽略每个参数类型的constvolatile类型说明符。

  • [temp.param] 指定:

    在确定模板参数的类型时,将忽略其上的顶级cv 限定符

  • [basic.life] 在指定如何重用生命周期结束的对象的存储时,提到:

    新对象的类型与原始对象相同(忽略顶级 CV 限定符)

  • [over.best.ics] 指定:

    顶级简历资格的任何差异都包含在初始化本身中,不构成转换。[示例:可以从类型为const A的参数初始化类型A的参数。这种情况的隐式转换序列是标识序列;它不包含从const AA的"转换"。]

  • [temp.deduct.call] 指定:

    如果A是符合 cv 条件的类型,则忽略A类型的顶级 cv 限定符以进行类型推断。

    和:

    如果P是符合 cv 条件的类型,则忽略P类型的顶级 cv 限定符以进行类型推断。

    ([temp.deduct.conv] 也有类似的语言。

  • [除了投掷] 指定:

    throw-expression初始化一个临时对象,其类型是通过从throw的操作数的静态类型中删除任何顶级cv 限定符并确定类型分别从"T数组"或"函数返回T"调整为"指向T的指针"或"指向返回T的函数的指针"来确定的。

  • [例外句柄] 指定:

    处理程序是具有E类型的对象的抛出表达式的匹配

    项,如果— 处理程序的类型为 cvT或 cvT&并且ET是同一类型(忽略顶级cv限定符),或者......

如果允许Dconst Dvolatile Dconst volatile D是不相关的类型,所有这些都必须改变。

因为如果操作数是多态对象,typeid返回动态类型。Cv 限定符被设计为编译时限制,并且必须在运行时记录其他信息才能检索动态类型的 cv 限定符,因此忽略顶级 cv 限定符是合理的。

对于类型或非多态对象的操作数,typeid忽略顶级 cv 限定符以保持一致性。否则,例如,以下assert将意外触发:

struct Base {
virtual ~Base() {}
};
struct Derived : public Base {};
const Derived *d = new Derived;
const Base *b = d;
assert(typeid(*b) == typeid(decltype(*d))); // should not fire