Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparing two type_info from typeid() operator

Tags:

c++

typeid

Is it OK to compare the results from two typeid() results? cppreference has this note about this operator:

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.

const std::type_info& ti1 = typeid(A);
const std::type_info& ti2 = typeid(A);

assert(&ti1 == &ti2); // not guaranteed
assert(ti1.hash_code() == ti2.hash_code()); // guaranteed
assert(std::type_index(ti1) == std::type_index(ti2)); // guaranteed

My understanding is that the the return is a reference to a static L value of type type_info. It's saying &ti1 == &ti2 is not guaranteed to be the same for the same types. It instead says to use the hash code or the std::type_index class. However it doesn't mention if comparing the types directly:

ti1 == ti2; 

is guaranteed to be true. I've used this before, does the documentation implicitly mean this is guaranteed?

like image 965
Zebrafish Avatar asked Nov 25 '18 13:11

Zebrafish


2 Answers

std::type_info is a class-type, which means that the ti1 == ti2 expression will trigger an overloaded operator==. Its behavior is described by [type.info]/p2:

bool operator==(const type_info& rhs) const noexcept;

Effects: Compares the current object with rhs.

Returns: true if the two values describe the same type.

like image 163
Piotr Skotnicki Avatar answered Sep 30 '22 09:09

Piotr Skotnicki


Some information on the implementation could be of interest: for g++/clang, the type_info starts with two pointers. The second one points to a fixed character string, which is the value returned by name().

** Note that this implementation is not required by the standard, and may vary across different targets for the same compiler.

Comparison is done by first checking if the type_info are at the same address; if so, they are equal; if not, next call strcmp() on the two 'name' strings. And the strcmp result determines the ordering for .before() method (and by extension, the ordering for type_index).

Usually, there is only one type_info in the program for any given type. But, when using shared libraries, it's possible to end up with one in a shared library, and another somewhere else. So, comparing the address is not sufficient to test whether two type_info represent the same type, nor can the address be used for ordering. If two type_info exist for the same type, their name() will return equivalent character strings, but those strings will be at different addresses, because the string constant and the type_info are generated together.

The .hash_code() method is disappointing: it calls a function to hash the name() string, character by character. g++ version calls strlen to find its len, and then calls the same function used for std::hash(std::string). And this happens even if the type is not unknown, as in e.g. typeid(std::complex<float>).hash_code()- where the compiler could, in principle, compute the result at compile time.

In my x86_64 clang++-9.0 installation, I'm seeing an odd result - hash_code() returns the same thing as name(), but cast to a size_t. This will often work, but will fail in cases where two type_info for the same type exist in the program. Also, it's not a very rich hash, consider the range of values which occur within a 64-bit address space. It's possible that my installation is somehow getting the wrong header files and this is the result, but it seems to work OK otherwise. Maybe this is an actual defect and nobody uses hash_code() because it's so slow...

I tried another clang-9 for a RISC processor, and it was similar to g++ for hash_code(), but didn't need to call strlen.

like image 26
greggo Avatar answered Sep 30 '22 09:09

greggo