19

Using C++ (gcc 4.8.3) I have 2 types (T1 and T2) which have the strange property that typeid(T1).name() and typeid(T2).name() are the same but std::is_same<T1, T2>::value is false.

How can that be? How can I investigate further to tell what the reason might be ?

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
Ludovic Aubert
  • 9,534
  • 4
  • 16
  • 28
  • 6
    `typeid` ignores top-level qualifiers. – Kerrek SB Apr 12 '16 at 15:30
  • 2
    ^^ which means things like const and & – Richard Hodges Apr 12 '16 at 15:30
  • @SergeyA so there is practically no use for typeid, or am i missing something? – 463035818_is_not_an_ai Apr 12 '16 at 15:35
  • 3
    @SergeyA There may be no guarantee that `typeid` will give you the *same* `std::type_info` object, but it *is* guaranteed that all `std::type_info` objects returned from `typeid(T)` for the same `T` compare equal. Per C++14 18.7.1/3, `std::type_info::operator==` returns `true` if the two operands describe the same type. – Angew is no longer proud of SO Apr 12 '16 at 15:45
  • @Angew, I agree. I will remove my comments. – SergeyA Apr 12 '16 at 15:59
  • 1
    @SergeyA: I'd call that a hostile reading of the standard. [type.info]/1 says "suitable for comparing two types for equality", and I think that pretty much precludes silly things like always returning `true`. – Kerrek SB Apr 12 '16 at 15:59
  • 2
    Why would `hash_code` be needed for comparison? It seems more plausible that `hash_code` was introduced to allow sticking typeids into unordered containers. Also, don't confuse the *name* with the value of the typeid result. Nobody is claiming that the *names* have the desired properties (and surely you would not want to hash the name). – Kerrek SB Apr 12 '16 at 16:04
  • @KerrekSB, I think, I was in the wrong all along. `hash_code` is actually expressed in the terms of equality of typeinfo. (however, with a better worded guarantee still). I fully admit my mistake. – SergeyA Apr 12 '16 at 16:08
  • @SergeyA: No worries - glad we're on the same page in the end. – Kerrek SB Apr 12 '16 at 16:20
  • @Kerrek SB Compiler implementors love hostile readings of the standard ;-) – Jesper Juhl Apr 12 '16 at 19:52

2 Answers2

16

Ignoring polymorphism, typeid() gives you an object representing the static type of the expression. But there are certain elements that are ignored when it comes to expression types. From [expr]:

If an expression initially has the type “reference to T” (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis. [...] If a prvalue initially has the type “cv T”, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.

As a result, any types which differ only in top-level cv-qualification or reference will yield the same typeid. For instance, the types int, const int, int& volatile const int&&, etc all give you the same typeid().

Basically, your initial thought process was:

typeid(T) == typeid(U) <==> std::is_same<T, U>

But the correct equivalence is:

typeid(T) == typeid(U) <==> std::is_same<expr_type<T>, expr_type<U>>

where:

template <class T>
using expr_type = std::remove_cv_t<std::remove_reference_t<T>>;
Barry
  • 286,269
  • 29
  • 621
  • 977
  • I think that another important difference is that (generally) `typeid` is a run time construct and `is_same` is a compile time construct. (I typically prefer compile time constructs). – Coder Aug 29 '23 at 21:40
6

typeid ignores all cv-qualifiers:

In all cases, cv-qualifiers are ignored by typeid (that is, typeid(T)==typeid(const T))

(ref)

This means that typeid ignores all references & and const (to name a few).

int i = 0;
const int&& j = 1;

if (typeid(i).hash_code() == typeid(j).hash_code()) //returns true
    std::cout << "typeid(int) == typeid(const int&&)";

Note that to compare 2 typeids, you have to use either typeid(T).hash_code() or std::type_index(typeid(T)), because only for those 2 functions is it guaranteed that 2 same typeids will be the same. Comparing references doesn't have that guarantee for example.

There is no guarantee that the same std::type_info instance will be referred to by all evaluations of the typeid expression on the same type, although std::type_info::hash_code of those type_info objects would be identical, as would be their std::type_index.

(ref)


As, @Yakk mentioned, you can use std::remove_reference and std::remove_cv to get the behavior you wanted.

std::remove_reference removes all references of T and std::remove_cv removes all const and volatile qualifiers. You should pass T through these functions before passing them to std::is_same, so that std::is_same only compares the underlying type (if any) of T1 and T2.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
  • 4
    Their `operator ==` is guaranteed to evaluate `true` for same-type descriptors too. So you cannot reliably compare `&typeid(T) == &typeid(T)`, but you *can* compare `typeid(T) == typeid(T)` and reliably get `true`. – Angew is no longer proud of SO Apr 12 '16 at 15:46
  • 1
    Mentioning `remove_reference` and `remove_cv` might help the OP solve their underlying problem (and not just their headline problem). – Yakk - Adam Nevraumont Apr 13 '16 at 02:10