C++. Run-Time Type Identification. The typeid operator

Run-Time Type Identification. The typeid operator. Examples


Contents


Search other resources:

1. The concept of Run-Time Type Identification

To support dynamic polymorphism, C++ introduced Run-Time Type Identification (RTTI). This dynamic identification allows the type of an object to be identified when a program is executed. The primary means of dynamic type identification is the typeid operator.

In order to provide better type casting, the latest versions of C++ include additional type casting operators. These operators include:

  • dynamic_cast;
  • const_cast;
  • reinterpret_cast;
  • static_cast.

Also, the C++ language has a standard type cast operator, which has the form

(type)expression

More information about this cast operator can be found here.

 

2. Type identification. The typeid operator

The C++ programming language supports dynamic polymorphism through a combination of inheritance, virtual functions, and the use of a pointer to the base class. In a class hierarchy, a base class pointer can point to an instance of any class in the hierarchy. Therefore, it is not always possible to guess which class object of the hierarchy the pointer points to. Type identification must be done during the execution of the program.

The typeid operator is used to identify a type in C++. This operator has two forms of use:

  • typeid(object) – defining the characteristics of the type of an object or a variable named object;
  • typeid(type) – defining characteristics of a type named type.

The typeid() operator returns a reference to an object of type type_info. The type_info class describes the type of an object and has the following internal members:

  1. Operator functions
bool operator==(const type_info& obj);
bool operator!=(const type_info& obj);

These functions overload the == and != comparison operators.

  1. Function
const char* name();

This function returns a pointer to the name of the type being defined.

  1. Function
bool before(const type_info& obj);

This function returns true if the calling object precedes the object given as the parameter.

To use the typeid() operator, you need to include the typeinfo module.

#include <typeinfo>

 

2.1. The typeid operator for an object
2.1.1. An example of using the typeid operator for base type variables and class objects

 

#include <iostream>
#include <typeinfo>
using namespace std;

// Some class
class MyClass
{
  // ...
};

void main()
{
  // Get characteristics of a type based on an object (instance)
  // 1. Declare variables (objects) of different types
  int a, b;
  double x;
  string s;
  MyClass obj;

  // 2. Use a reference to type_info to get information about a type
  // 2.1. Method name() - get the type name
  const type_info& ti = typeid(x);
  cout << "Type of x is: " << ti.name() << endl;

  // 2.2. Compare object types s and obj
  const type_info& ti_s = typeid(s);
  const type_info& ti_obj = typeid(obj);

  if (ti_s == ti_obj)
    cout << "typeid(s)==typeid(obj)" << endl;
  else
    cout << "typeid(s)!=typeid(obj)" << endl;

  // 3. Direct use of typeid() without declaring a reference
  // 3.1. Compare object types a, b - use operator==() function
  if (typeid(a) == typeid(b))
    cout << "Types of a and b are equal." << endl;
  else
    cout << "Types of a and b are not equal." << endl;

  // 3.2. Compare the types of variables x and a use the operator!=() function
  if (typeid(x) != typeid(a))
    cout << "Types of x and a are not equal." << endl;
  else
    cout << "Types of x and a are equal." << endl;

  // 3.3. Get data based on type
  string tName = typeid(s).name();
  cout << "typeid(string) = " << tName << endl;
}

Program result

Type of x is: double
typeid(s)!=typeid(obj)
Types of a and b are equal.
Types of x and a are not equal.
typeid(string) = class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >

 

2.1.2. An example of using the typeid operator for objects of classes that form a hierarchy

The typeid operator is useful when classes form a hierarchy and virtual functions are implemented in the classes. The following example declares a base class, Area, and two derived classes, Circle and Rectangle. All 3 classes contain an implementation of the GetArea() virtual function, which returns different area values depending on the selected shape.

For demonstration purposes, the function Demo_ref_typeid() is implemented, which receives a reference to the base class Area. The function calls the typeid operator, which determines the type of the instance passed to the function. The instance type depends on which instance in the class hierarchy was passed when calling the Demo_ref_typeid() function.

The main() function demonstrates using the typeid() operator for class objects and passing it to the Demo_ref_typeid() function.

#include <iostream>
#include <typeinfo>
using namespace std;

// Applying typeid to the class hierarchy
// polymorphic class Area
class Area
{
public:
  virtual double GetArea() { return 0; }; // virtual function
};

class Circle :public Area
{
  double r;

public:
  Circle(double _r)
  {
    if (_r > 0)
      r = _r;
    else
      r = 1;
  }

  double GetArea()
  {
    const double pi = 3.1415;
    return pi * r * r;
  }
};

// Rectangle
class Rectangle :public Area
{
  double a, b;

public:
  Rectangle(double _a, double _b)
  {
    if ((_a >= 0) && (_b >= 0))
    {
      a = _a; b = _b;
    }
    else
    {
      a = b = 0.0;
    }
  }

  double GetArea()
  {
    return a * b;
  }
};

// Passing a reference to the base class to the method,
// if the class is polymorphic, then the type of the reference // is determined by the type of the parameter-object
void Demo_ref_typeid(Area& ref)
{
  cout << "The type of ref is: ";
  cout << typeid(ref).name() << endl;
}

void main(void)
{
  Area* p; // pointer to the base class
  Area a;
  Circle c(3);
  Rectangle r(7, 5);

  // this is not possible, the uninitialized value of p
  // cout << typeid(*p).name() << endl;
  p = &a;
  cout << "Pointer p points to object of type: ";
  cout << typeid(*p).name() << endl; // class Area

  p = &c;
  cout << "Pointer p points to object of type: ";
  cout << typeid(*p).name() << endl; // class Circle

  p = &r;
  cout << "Pointer p points to object of type: ";
  cout << typeid(*p).name() << endl; // class Rectangle

  // Important: if you remove the word virtual in the Area class,
  // it will display the following result:
  // Pointer p points to object of type: class Area
  // Pointer p points to object of type: class Area
  // Pointer p points to object of type: class Area

  // Demonstration of using typeid for references
  Demo_ref_typeid(a); // The type of ref is: class Area
  Demo_ref_typeid(c); // The type of ref is: class Circle
  Demo_ref_typeid(r); // The type of ref is: class Rectangle
}

If in the above code the Demo_ref_typeid() method receives a reference to a base class that does not support polymorphism (there is no chain of virtual functions), then the type of the base class Area will be obtained.

The implementation of the Demo_ref_typeid() function receives a reference parameter to the base class Area&. The same result can be achieved by taking a pointer parameter to the base class Area*.

After running the program will give the following result

Pointer p points to object of type: class Area
Pointer p points to object of type: class Circle
Pointer p points to object of type: class Rectangle
The type of ref is: class Area
The type of ref is: class Circle
The type of ref is: class Rectangle

 

2.2. The typeid operator for a type (class)

The example shows code that uses the typeid operator to determine the type of an object of some class and variable.

#include <iostream>
#include <typeinfo>
using namespace std;

// A set of classes
// Base class
class A
{
  // ...
};

// Inherited classes
class B : public A
{
  // ...
};

class C : public B
{
  // ...
};

void main(void)
{
  // 1. Create instances of classes B, C
  B objB;
  C objC;

  // 2. Checking what type an objC instance is
  if (typeid(objC) == typeid(A))
    cout << "objC is of type A" << endl;
  if (typeid(objC) == typeid(B))
    cout << "objC is of type B" << endl;
  if (typeid(objC) == typeid(C))
    cout << "objC is of type C" << endl; // +

  // 3. Checking if a is an integer type int
  int a;

  if (typeid(a) == typeid(int))
    cout << "Variable a is of type int." << endl;
  else
    cout << "Variable a is not of type int" << endl;
}

Program result

objC is of type C
Variable a is of type int.

 

3. An example of using the typeid operator for template classes

The typeid operator can be applied to template classes that operate on some generic type. For each specific instance of the class, a suitable type is formed.

The following is an example of using the typeid() operator for the Point<T> template class.

 

#include <iostream>
#include <typeinfo>
using namespace std;

// Template class that implements a point on the coordinate plane
template <class T>
class Point
{
private:
  T x, y;

public:
  // Constructor
  Point(T _x, T _y) : x(_x), y(_y) { }

  // Access methods
  T X() { return x; }
  T Y() { return y; }
};

void main(void)
{
  // Declare instances of the Point<T> class for different types
  Point<int> objInt(8, 9);
  Point<double> objDouble(7.2, 3.1);

  // Print object types objInt and objDouble
  cout << "typeid(objInt) = " << typeid(objInt).name() << endl;
  cout << "typeid(objDouble) = " << typeid(objDouble).name() << endl;

  // Determine if objInt and objDouble object types are the same
  if (typeid(objInt) == typeid(objDouble))
    cout << "Types of objInt and objDouble are equal." << endl;
  else
    cout << "Types of objInt and objDouble are not equal." << endl;
}

Program result

typeid(objInt) = class Point<int>
typeid(objDouble) = class Point<double>
Types of objInt and objDouble are not equal.

 


Related topics