来自C ++语言标准,来自ISO / IEC 14882:2003的§5.2.8,引用,
总是忽略左值表达式的顶级cv限定符或作为typeid操作数的type-id。
这个决定的理由是什么?它在类型系统中打了一个洞,反直觉。 const信息(或易失性部分)在编译时是已知的,因此如果typeid只考虑了cv限定符,就没有开销。
答案 0 :(得分:2)
尽管能够使用const
,volatile
和const volatile
变体(请参阅[class.mfct.nonstatic])重载非静态成员函数,但请键入D
,{ {1}},const D
和volatile D
(对于某些非cv限定类型const volatile D
)并不相关;标准在[basic.type.qualifier]中指定,对于每个 cv-unqualified type ,有一个不同的D
- 限定的,const
- 限定的,{{1 } - - 合格"版本"具有相同表示和对齐要求的类型。如果volatile
和const volatile
可能彼此无关(例如,通过具有不同的表示,存储要求,或者在类,不同成员的情况下),那么很多语言都会打破了。
例如,D
类型的对象可隐式转换为const D
,D&
和const D&
(请参阅[conv.qual])。但是,如果允许volatile D&
与const volatile D&
无关,则此转换无效。
还要考虑标准中的许多地方指定忽略顶级cv限定符:
[over.load]指定:
仅在
D
和/或const D
存在或不存在时不同的参数声明是等效的。也就是说,在确定声明,定义或调用哪个函数时,将忽略每个参数类型的const
和volatile
类型说明符。
[temp.param]指定:
模板参数上的顶级 cv-qualifiers 在确定其类型时会被忽略。
[basic.life],在指定如何重复使用其生命周期结束的对象的存储时,请注意:
新对象与原始对象的类型相同(忽略顶级cv限定符)
[over.best.ics]指定:
顶级cv资格的任何差异都归入初始化本身,并不构成转换。 [示例:类型
const
的参数可以从类型volatile
的参数初始化。该情况的隐式转换序列是标识序列;它不包含从A
到const A
的“转化”。 ]
[temp.deduct.call]指定:
如果
const A
是cv限定类型,则类型扣除将忽略A
类型的顶级cv限定符。
和
如果
A
是cv限定类型,则类型扣除将忽略A
类型的顶级cv限定符。
([temp.deduct.conv]有类似的语言。)
[except.throw]指定:
throw-expression 初始化一个临时对象,其类型是通过从{的操作数的静态类型中删除任何顶级 cv-qualifiers 来确定的。 {1}}并将类型从“
P
数组”或“返回P
的函数”调整为“指向throw
的指针”或“返回T
的函数指针”分别。
[except.handle]指定:
处理程序是 throw-expression 与
T
类型对象的匹配,如果- 处理程序的类型为 cv
T
或 cvT
,而E
和T
是相同类型(忽略顶级cv限定符),或......
如果T&
,E
,T
和D
被允许为不相关的类型,则必须更改所有这些内容。
答案 1 :(得分:1)
因为如果操作数是polymorphic object,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