Difference between revisions of "cpp/language/typeid"
(Add a remarks regarding the absence of guarantee in the standard regarding the returned type_info instance uniqueness.) |
m (tt title's keyword) |
||
(38 intermediate revisions by 21 users not shown) | |||
Line 1: | Line 1: | ||
− | {{title|typeid operator}} | + | {{title|{{tt|typeid}} operator}} |
{{cpp/language/expressions/navbar}} | {{cpp/language/expressions/navbar}} | ||
+ | |||
Queries information of a type. | Queries information of a type. | ||
− | Used where the | + | Used where the {{rlp|type#Dynamic type|dynamic type}} of a {{rlp|object#Polymorphic objects|polymorphic object}} must be known and for static type identification. |
===Syntax=== | ===Syntax=== | ||
− | |||
{{sdsc begin}} | {{sdsc begin}} | ||
− | {{sdsc | num=1 | {{ttb|typeid(}} {{spar|type}} {{ttb|)}}}} | + | {{sdsc|num=1|{{ttb|typeid (}} {{spar|type}} {{ttb|)}}}} |
− | {{sdsc | num=2 | {{ttb|typeid(}} {{spar|expression}} {{ttb|)}}}} | + | {{sdsc|num=2|{{ttb|typeid (}} {{spar|expression}} {{ttb|)}}}} |
{{sdsc end}} | {{sdsc end}} | ||
− | The | + | The {{c/core|typeid}} expression is an {{rlp|value category|lvalue expression}} which refers to an object with {{rlp|static|static storage duration}}, of const-qualified version of the polymorphic type {{lc|std::type_info}} or some type derived from it. |
− | + | If the standard library definition of {{lc|std::type_info}} is not visible when using {{c/core|typeid}}, the program is ill-formed. | |
− | + | ||
===Explanation=== | ===Explanation=== | ||
+ | If {{spar|type}} or the type of {{spar|expression}} is a class type or a reference to a class type, then that class type cannot be an {{rlp|incomplete type}}. | ||
− | @1@ Refers to a {{ | + | @1@ Refers to a {{lc|std::type_info}} object representing {{spar|type}}. If {{spar|type}} is a reference type, the result refers to a {{lc|std::type_info}} object representing the cv-unqualified version of the referenced type. |
+ | @2@ Examines {{spar sep|expression}}: | ||
+ | * If {{spar|expression}} is {{rev inl|until=c++11|an {{rlp|value category#lvalue|lvalue}}}}{{rev inl|since=c++11|a {{rlp|value category#glvalue|glvalue}}}} expression that identifies an {{rlp|object#Polymorphic objects|object of a polymorphic type}} (that is, a class that declares or inherits at least one {{rlp|virtual|virtual function}}), the {{c/core|typeid}} expression evaluates the expression and then refers to the {{lc|std::type_info}} object that represents the dynamic type of the expression. | ||
+ | :* If {{spar|expression}} is an {{rlp|operator member access#Built-in indirection operator|indirection expression}} and its operand evaluates to a {{rlp|pointer#Null pointers|null pointer value}}, an exception of a type matching handlers of type {{lc|std::bad_typeid}} is thrown<ref>In other contexts, evaluating such an {{spar|expression}} results in undefined behavior.</ref>. | ||
+ | * Otherwise, {{c/core|typeid}} does {{rlp|expressions#Potentially-evaluated expressions|not evaluate the expression}}, and the {{lc|std::type_info}} object it identifies represents the static type of the expression. Lvalue-to-rvalue, array-to-pointer, or function-to-pointer conversions are not performed. | ||
+ | {{rrev|since=c++17| | ||
+ | :*{{rlps|implicit conversion#Temporary materialization}}, however, is (formally) performed for prvalue arguments: the argument must be destructible in the context in which the {{c/core|typeid}} expression appears. | ||
+ | }} | ||
− | + | If {{spar|type}} or the type of {{spar|expression}} is cv-qualified, the result of the {{c/core|typeid}} refers to a {{lc|std::type_info}} object representing the cv-unqualified type (that is, {{c|1=typeid(const T) == typeid(T)}}). | |
− | + | If {{c/core|typeid}} is used on an object under construction or destruction (in a destructor or in a constructor, including constructor's {{rlp|initializer list}} or {{rlp|data members#Member initialization|default member initializers}}), then the {{lc|std::type_info}} object referred to by this {{c/core|typeid}} represents the class that is being constructed or destroyed even if it is not the most-derived class. | |
− | + | <references/> | |
− | + | ===Notes=== | |
+ | When applied to an expression of polymorphic type, evaluation of a typeid expression may involve runtime overhead (a virtual table lookup), otherwise typeid expression is resolved at compile time. | ||
− | {{ | + | It is unspecified whether the destructor for the object referred to by {{c/core|typeid}} is executed at the end of the program. |
− | + | There is no guarantee that the same {{lc|std::type_info}} object will be referred to by all evaluations of the typeid expression on the same type, although they would compare equal, {{lc|std::type_info::hash_code}} of those {{tt|type_info}} objects would be identical, as would be their {{lc|std::type_index}}. | |
+ | {{source|1= | ||
+ | const std::type_info& ti1 = typeid(A); | ||
+ | const std::type_info& ti2 = typeid(A); | ||
+ | |||
+ | assert(&ti1 == &ti2); // not guaranteed | ||
+ | assert(ti1 == ti2); // guaranteed | ||
+ | assert(ti1.hash_code() == ti2.hash_code()); // guaranteed | ||
+ | assert(std::type_index(ti1) == std::type_index(ti2)); // guaranteed | ||
+ | }} | ||
===Keywords=== | ===Keywords=== | ||
{{ltt|cpp/keyword/typeid}} | {{ltt|cpp/keyword/typeid}} | ||
− | |||
− | |||
− | |||
− | |||
− | |||
===Example=== | ===Example=== | ||
− | {{example | | + | {{example|The example showing output using one of the implementations where {{lc|type_info::name}} returns full type names; filter through c++filt -t if using gcc or similar. |
− | | p=true | + | |p=true |
− | | code= | + | |code= |
#include <iostream> | #include <iostream> | ||
#include <string> | #include <string> | ||
Line 60: | Line 72: | ||
std::string mystr = "string"; | std::string mystr = "string"; | ||
double *mydoubleptr = nullptr; | double *mydoubleptr = nullptr; | ||
− | + | ||
std::cout << "myint has type: " << typeid(myint).name() << '\n' | std::cout << "myint has type: " << typeid(myint).name() << '\n' | ||
<< "mystr has type: " << typeid(mystr).name() << '\n' | << "mystr has type: " << typeid(mystr).name() << '\n' | ||
<< "mydoubleptr has type: " << typeid(mydoubleptr).name() << '\n'; | << "mydoubleptr has type: " << typeid(mydoubleptr).name() << '\n'; | ||
− | + | ||
// std::cout << myint is a glvalue expression of polymorphic type; it is evaluated | // std::cout << myint is a glvalue expression of polymorphic type; it is evaluated | ||
− | const std::type_info& r1 = typeid(std::cout << myint); | + | const std::type_info& r1 = typeid(std::cout << myint); // side-effect: prints 50 |
− | std::cout << "std::cout<<myint has type : " << r1.name() << '\n'; | + | std::cout << '\n' << "std::cout<<myint has type : " << r1.name() << '\n'; |
− | + | ||
// std::printf() is not a glvalue expression of polymorphic type; NOT evaluated | // std::printf() is not a glvalue expression of polymorphic type; NOT evaluated | ||
const std::type_info& r2 = typeid(std::printf("%d\n", myint)); | const std::type_info& r2 = typeid(std::printf("%d\n", myint)); | ||
std::cout << "printf(\"%d\\n\",myint) has type : " << r2.name() << '\n'; | std::cout << "printf(\"%d\\n\",myint) has type : " << r2.name() << '\n'; | ||
− | + | ||
// Non-polymorphic lvalue is a static type | // Non-polymorphic lvalue is a static type | ||
Derived d1; | Derived d1; | ||
Base& b1 = d1; | Base& b1 = d1; | ||
std::cout << "reference to non-polymorphic base: " << typeid(b1).name() << '\n'; | std::cout << "reference to non-polymorphic base: " << typeid(b1).name() << '\n'; | ||
− | + | ||
Derived2 d2; | Derived2 d2; | ||
Base2& b2 = d2; | Base2& b2 = d2; | ||
std::cout << "reference to polymorphic base: " << typeid(b2).name() << '\n'; | std::cout << "reference to polymorphic base: " << typeid(b2).name() << '\n'; | ||
− | + | ||
− | try { | + | try |
− | // dereferencing a null pointer: okay for a non- | + | { |
+ | // dereferencing a null pointer: okay for a non-polymorphic expression | ||
std::cout << "mydoubleptr points to " << typeid(*mydoubleptr).name() << '\n'; | std::cout << "mydoubleptr points to " << typeid(*mydoubleptr).name() << '\n'; | ||
// dereferencing a null pointer: not okay for a polymorphic lvalue | // dereferencing a null pointer: not okay for a polymorphic lvalue | ||
− | Derived2* bad_ptr = | + | Derived2* bad_ptr = nullptr; |
std::cout << "bad_ptr points to... "; | std::cout << "bad_ptr points to... "; | ||
std::cout << typeid(*bad_ptr).name() << '\n'; | std::cout << typeid(*bad_ptr).name() << '\n'; | ||
− | } catch(const std::bad_typeid& e) { | + | } |
− | + | catch (const std::bad_typeid& e) | |
+ | { | ||
+ | std::cout << " caught " << e.what() << '\n'; | ||
} | } | ||
} | } | ||
− | + | |output= | |
+ | ======== output from Clang ======== | ||
+ | myint has type: i | ||
+ | mystr has type: NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE | ||
+ | mydoubleptr has type: Pd | ||
+ | 50 | ||
+ | std::cout<<myint has type : NSt3__113basic_ostreamIcNS_11char_traitsIcEEEE | ||
+ | printf("%d\n",myint) has type : i | ||
+ | reference to non-polymorphic base: 4Base | ||
+ | reference to polymorphic base: 8Derived2 | ||
+ | mydoubleptr points to d | ||
+ | bad_ptr points to... caught std::bad_typeid | ||
+ | |||
+ | ======== output from MSVC ======== | ||
myint has type: int | myint has type: int | ||
− | mystr has type: std::basic_string<char, std::char_traits<char>, std::allocator<char> > | + | mystr has type: class std::basic_string<char,struct std::char_traits<char>,⮠ |
− | mydoubleptr has type: double* | + | class std::allocator<char> > |
− | + | mydoubleptr has type: double * __ptr64 | |
+ | 50 | ||
+ | std::cout<<myint has type : class std::basic_ostream<char,struct std::char_traits<char> > | ||
printf("%d\n",myint) has type : int | printf("%d\n",myint) has type : int | ||
− | reference to non-polymorphic base: Base | + | reference to non-polymorphic base: struct Base |
− | reference to polymorphic base: Derived2 | + | reference to polymorphic base: struct Derived2 |
mydoubleptr points to double | mydoubleptr points to double | ||
− | bad_ptr points to... caught | + | bad_ptr points to... caught Attempted a typeid of nullptr pointer! |
}} | }} | ||
− | + | ===Defect reports=== | |
− | + | {{dr list begin}} | |
− | + | {{dr list item|wg=cwg|dr=492|std=C++98|before=when {{c/core|typeid}} is applied to a reference to cv-qualified<br>type, the result represented the referenced type|after=the result represents the<br>cv-unqualified referenced type}} | |
− | + | {{dr list item|wg=cwg|dr=1416|std=C++98|before=the wording regarding top-level<br>cv-qualification might be misinterpreted|after=improved the wording}} | |
− | + | {{dr list item|wg=cwg|dr=1431|std=C++98|before={{c/core|typeid}} was only allowed to throw {{lc|std::bad_typeid}}|after=allowed to throw<br>matchable derived classes}} | |
− | + | {{dr list item|wg=cwg|dr=1954|std=C++98|before=it was unclear whether null pointer dereference<br>can be checked in subexpressions of {{spar|expression}}|after=only checked at top level}} | |
− | + | {{dr list end}} | |
− | + | ||
+ | ===See also=== | ||
+ | {{dsc begin}} | ||
+ | {{dsc inc|cpp/types/dsc type_info}} | ||
+ | {{dsc end}} | ||
+ | |||
+ | {{langlinks|de|es|fr|it|ja|pt|ru|zh}} |
Latest revision as of 16:37, 12 August 2024
Queries information of a type.
Used where the dynamic type of a polymorphic object must be known and for static type identification.
Contents |
[edit] Syntax
typeid ( type )
|
(1) | ||||||||
typeid ( expression )
|
(2) | ||||||||
The typeid expression is an lvalue expression which refers to an object with static storage duration, of const-qualified version of the polymorphic type std::type_info or some type derived from it.
If the standard library definition of std::type_info is not visible when using typeid, the program is ill-formed.
[edit] Explanation
If type or the type of expression is a class type or a reference to a class type, then that class type cannot be an incomplete type.
- If expression is an lvalue(until C++11)a glvalue(since C++11) expression that identifies an object of a polymorphic type (that is, a class that declares or inherits at least one virtual function), the typeid expression evaluates the expression and then refers to the std::type_info object that represents the dynamic type of the expression.
- If expression is an indirection expression and its operand evaluates to a null pointer value, an exception of a type matching handlers of type std::bad_typeid is thrown[1].
- Otherwise, typeid does not evaluate the expression, and the std::type_info object it identifies represents the static type of the expression. Lvalue-to-rvalue, array-to-pointer, or function-to-pointer conversions are not performed.
|
(since C++17) |
If type or the type of expression is cv-qualified, the result of the typeid refers to a std::type_info object representing the cv-unqualified type (that is, typeid(const T) == typeid(T)).
If typeid is used on an object under construction or destruction (in a destructor or in a constructor, including constructor's initializer list or default member initializers), then the std::type_info object referred to by this typeid represents the class that is being constructed or destroyed even if it is not the most-derived class.
- ↑ In other contexts, evaluating such an expression results in undefined behavior.
[edit] Notes
When applied to an expression of polymorphic type, evaluation of a typeid expression may involve runtime overhead (a virtual table lookup), otherwise typeid expression is resolved at compile time.
It is unspecified whether the destructor for the object referred to by typeid is executed at the end of the program.
There is no guarantee that the same std::type_info object will be referred to by all evaluations of the typeid expression on the same type, although they would compare equal, std::type_info::hash_code of those type_info
objects would be identical, as would be their std::type_index.
const std::type_info& ti1 = typeid(A); const std::type_info& ti2 = typeid(A); assert(&ti1 == &ti2); // not guaranteed assert(ti1 == ti2); // guaranteed assert(ti1.hash_code() == ti2.hash_code()); // guaranteed assert(std::type_index(ti1) == std::type_index(ti2)); // guaranteed
[edit] Keywords
[edit] Example
The example showing output using one of the implementations where type_info::name returns full type names; filter through c++filt -t if using gcc or similar.
#include <iostream> #include <string> #include <typeinfo> struct Base {}; // non-polymorphic struct Derived : Base {}; struct Base2 { virtual void foo() {} }; // polymorphic struct Derived2 : Base2 {}; int main() { int myint = 50; std::string mystr = "string"; double *mydoubleptr = nullptr; std::cout << "myint has type: " << typeid(myint).name() << '\n' << "mystr has type: " << typeid(mystr).name() << '\n' << "mydoubleptr has type: " << typeid(mydoubleptr).name() << '\n'; // std::cout << myint is a glvalue expression of polymorphic type; it is evaluated const std::type_info& r1 = typeid(std::cout << myint); // side-effect: prints 50 std::cout << '\n' << "std::cout<<myint has type : " << r1.name() << '\n'; // std::printf() is not a glvalue expression of polymorphic type; NOT evaluated const std::type_info& r2 = typeid(std::printf("%d\n", myint)); std::cout << "printf(\"%d\\n\",myint) has type : " << r2.name() << '\n'; // Non-polymorphic lvalue is a static type Derived d1; Base& b1 = d1; std::cout << "reference to non-polymorphic base: " << typeid(b1).name() << '\n'; Derived2 d2; Base2& b2 = d2; std::cout << "reference to polymorphic base: " << typeid(b2).name() << '\n'; try { // dereferencing a null pointer: okay for a non-polymorphic expression std::cout << "mydoubleptr points to " << typeid(*mydoubleptr).name() << '\n'; // dereferencing a null pointer: not okay for a polymorphic lvalue Derived2* bad_ptr = nullptr; std::cout << "bad_ptr points to... "; std::cout << typeid(*bad_ptr).name() << '\n'; } catch (const std::bad_typeid& e) { std::cout << " caught " << e.what() << '\n'; } }
Possible output:
======== output from Clang ======== myint has type: i mystr has type: NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE mydoubleptr has type: Pd 50 std::cout<<myint has type : NSt3__113basic_ostreamIcNS_11char_traitsIcEEEE printf("%d\n",myint) has type : i reference to non-polymorphic base: 4Base reference to polymorphic base: 8Derived2 mydoubleptr points to d bad_ptr points to... caught std::bad_typeid ======== output from MSVC ======== myint has type: int mystr has type: class std::basic_string<char,struct std::char_traits<char>,⮠ class std::allocator<char> > mydoubleptr has type: double * __ptr64 50 std::cout<<myint has type : class std::basic_ostream<char,struct std::char_traits<char> > printf("%d\n",myint) has type : int reference to non-polymorphic base: struct Base reference to polymorphic base: struct Derived2 mydoubleptr points to double bad_ptr points to... caught Attempted a typeid of nullptr pointer!
[edit] Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 492 | C++98 | when typeid is applied to a reference to cv-qualified type, the result represented the referenced type |
the result represents the cv-unqualified referenced type |
CWG 1416 | C++98 | the wording regarding top-level cv-qualification might be misinterpreted |
improved the wording |
CWG 1431 | C++98 | typeid was only allowed to throw std::bad_typeid | allowed to throw matchable derived classes |
CWG 1954 | C++98 | it was unclear whether null pointer dereference can be checked in subexpressions of expression |
only checked at top level |
[edit] See also
contains some type’s information, the class returned by the typeid operator (class) |