The typeid operator returns a const reference to a type_info object that describes type-id or the type of expression. If expression is an lvalue (not a pointer) of a polymorphic class, the type_info of the most-derived class is returned. Otherwise, expression is not evaluated, and the type_info of its static type is returned.

Each distinct type has its own associated type_info object, but type synonyms (such as those created with typedef) have the same type_info object. 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. 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



// Non-polymorphic
struct Base {};
struct Derived : Base {};
// Polymorphic 
struct Base2 { virtual void foo() {} }; 
struct Derived2 : Base2 {};
int main() {

  int myint = 50;
  std::string mystr = "string";
  double *mydoubleptr = nullptr;

  // output : int
  std::cout << typeid(myint).name();
  // Non-polymorphic lvalue is a static type
  Derived d1;
  Base& b1 = d1;
  // Output : Base
  std::cout << typeid(b1).name();
  // Polymorphic lvalue is a static type
  Derived2 d2;
  Base2& b2 = d2;
  // Output : Derived2
  std::cout << typeid(b2).name();