问题
I am reading the C++11 draft standard and the section on [expr.typeid] mentions the following (emphasis mine):
[...]
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.
Further in p5 of the same section, it goes on to give the following example:
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)
Given the following code sample:
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)
}
My understanding is that [expr.typeid]p4 talks only about the form typeid(type-id) and bar
in typeid(bar)
is an id-expression and not a type-id. Why does (3) above evaluate to true
? Which text in the standard covers this? What have I missed?
回答1:
The answer is in [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.
So when we get into [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]).
The id-expression in question is already of the referenced type when typeid
examines it.
回答2:
Much of C++'s expression "system" works this way. You're observing just one example of a rule that's interwoven throughout the language: references sometimes aren't really "things" in the context of an expression; they just refer to an extant object in the same way that its original name of declaration would. They're intended to be "transparent" at some layers, and this is evident in some of the inner workings.
It may seem counter-intuitive in places; for example, though std::move(expr)
returns a T&&
, the resulting expression is an rvalue T
(not a T&&
) … it is the fact that this expression is an rvalue that allows it to bind to rvalue reference parameters when you later pass it to some function. (There is a common misconception that it is the matching type T&&
that makes this work.)
typeid
is arguably another counter-intuitive example. The specific rule you're missing is [expr.type], responsible for "decaying" these reference types in an expression before any other processing. At that point, the value category of the expression takes on important meaning, and this value category is at least partially determined by the original, unadjusted type. This is how the type and value category of expressions evolves as data moves through your program.
(The subsequent stripping of the const
is part of the rules for typeid that you already quoted.)
(The rules for typeid
when passed a type are also distinct).
Usually, we don't have to worry about this. Unfortunately, there are several places where you can observe it that feel a bit like an abstraction leak. You've found one of those places. The rules do make everything come together in the end though.
来源:https://stackoverflow.com/questions/62134074/why-does-typeidx-typeidy-evaluate-to-true-where-x-and-y-are-id-exp