为什么 'typeid(x) == typeid(y)' 的计算结果为真,其中 'x' 和 'y' 分别是 T 和 T& 类型的 id-expression?

Why does 'typeid(x) == typeid(y)' evaluate to true, where 'x' and 'y' are id-expression of type T and T& respectively?

我正在阅读 C++11 标准草案,[expr.typeid] 部分提到以下内容(强调我的):

[...]

When typeid is applied to an expression other than a glvalue of a polymorphic class type, the result refers to a std::type_info object representing the static type of the expression. Lvalue-to-rvalue (4.1), array-topointer (4.2), and function-to-pointer (4.3) conversions are not applied to the expression. If the type of the expression is a class type, the class shall be completely-defined. The expression is an unevaluated operand (Clause 5).

When typeid is applied to a type-id, the result refers to a std::type_info object representing the type of the type-id. If the type of the type-id is a reference to a possibly cv-qualified type, the result of the typeid expression refers to a std::type_info object representing the cv-unqualified referenced type. If the type of the type-id is a class type or a reference to a class type, the class shall be completely-defined.

在同一节的p5中,继续给出以下示例:

class D { /* ... */ };
D d1;
const D d2;

typeid(d1) == typeid(d2); // yields true
typeid(D)  == typeid(const D); // yields true
typeid(D)  == typeid(d2); // yields true
typeid(D)  == typeid(const D&); // yields true   -- (1)


给定以下代码示例:

int main()
{
    int foo = 42;
    int &bar = foo;
    bool comp1 = (typeid(int) == typeid(int&));    // Yields true, same as (1)   -- (2) 
    bool comp2 = (typeid(foo) == typeid(bar));     // Yields true, Why?          -- (3)
}


我的理解是[expr.typeid]p4只讲了形式typeid(type-id)typeid(bar) 中的 barid-expression 而不是 type-id.为什么上面的 (3) 计算为 true?标准中的哪个文本涵盖了这一点?我错过了什么?

答案在[expr]

5 If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.

所以当我们进入 [expr.typeid]

3 When typeid is applied to an expression other than a glvalue of a polymorphic class type, the result refers to a std::type_info object representing the static type of the expression. Lvalue-to-rvalue ([conv.lval]), array-to-pointer ([conv.array]), and function-to-pointer ([conv.func]) conversions are not applied to the expression. If the type of the expression is a class type, the class shall be completely-defined. The expression is an unevaluated operand (Clause [expr]).

typeid 检查时,相关的 id 表达式已经是引用类型。

很多 C++ 表达式 "system" 都是这样工作的。您观察到的只是贯穿整个语言的规则的一个示例:在表达式的上下文中,引用有时并不是真正的 "things";它们只是以与其声明的原始名称相同的方式引用现存对象。它们旨在 "transparent" 在某些层,这在某些内部工作中很明显。

在某些地方可能看起来违反直觉;例如,尽管 std::move(expr) returns a T&&,结果表达式是右值 T (not a T&& ) ... 事实上,这个表达式是一个右值,当你稍后将它传递给某个函数时,它允许它绑定到右值引用参数。 (有一个常见的误解,认为是匹配类型 T&& 才起作用。)

typeid 可以说是另一个违反直觉的例子。您缺少的特定规则是 [expr.type],负责在任何其他处理之前表达式中的 "decaying" 这些引用类型。此时,表达式的 值类别 具有重要意义,并且该值类别至少部分由原始的、未调整的类型确定。这就是表达式的类型和值类别如何随着数据在您的程序中移动而演变。

(随后剥离的 const 是您已经引用的 the rules for typeid 的一部分。)

(传递类型 are also distincttypeid 的规则)。

通常,我们不必担心这个。不幸的是,有几个地方你可以观察到它感觉有点像抽象泄漏。你找到了其中一个地方。不过,规则确实让一切最终都走到了一起。