Shape라는 parent 클래스가 있고 그 하위로 Triangle, Rectangle, Pentagon이라는 클래스가 있다고 가정합니다.


struct Shape {};
struct Triangle : public Shape {};
struct Rectangle : public Shape {};
struct Pentagon : public Shape {};




object의 type을 찾아 내어 적당한 처리를 하는 방법 중의 가장 대표적인 예가 바로 dynamic_cast를 사용하는 것입니다(물론 클래스 내부에는 virtual function이 최소 하나 이상 있음을 가정합니다). 그런데 dynamic_cast는 multiple inheritance까지 처리해야 하므로 내부적으로 복잡한 연산을 수행하게 됩니다.


void checkShape(Shape* shape) {
  if ((dynamic_cast<Triangle*>(shape)) != nullptr) cout << "This is a triangle\n";
  else if ((dynamic_cast<Rectangle*>(shape)) != nullptr) cout << "This is a rectangle\n";
  else if ((dynamic_cast<Pentagon*>(shape)) != nullptr) cout << "This is a pentagon\n";
  else cout << "None of them\n";
}




두 번째 방식으로 typeid라는 operator를 이용하여 처리할 수도 있습니다.


void checkShape2(Shape* shape) {
  const std::type_info& ti =  typeid(*shape);
  if (ti == typeid(Triangle)) cout << "This is a triangle\n";
  else if (ti == typeid(Rectangle)) cout << "This is a rectangle\n";
  else if (ti == typeid(Pentagon)) cout << "This is a pentagon\n";
  else cout << "None of them\n";
}




그런데, type_info에서 형 비교를 할 때에는 == operator를 사용해야 하고, == operator 내부에서는 strcmp 류의 함수가 호출이 됩니다. 클래스 명이 길면 그만큼 속도가 걸린다는 얘기.


class type_info {
    ...
    bool operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT
    {
      return ((__name == __arg.__name)
	      || (__name[0] != '*' &&
		  __builtin_strcmp (__name, __arg.__name) == 0));
    }
    ...
}




마지막으로 constexpr specifier를 이용하여 다음과 같이 사용할 수 있습니다. 아래 코드의 모든 클래스의 id()라는 function에서 반환하는 값은 compile time에 결정이 되지 runtime에 결정이 되는 것이 아닙니다. checkShape3 function의  switch 내부에서 함수의 실행 결과를 case의 value로 사용할 수 있는 것도 이러한 이유때문입니다. 


struct Shape {
  static constexpr uint32_t const_hash(const char *p) {
    return *p ? static_cast<uint32_t>(*p) + 33 * const_hash(p + 1) :  5381;
  }

  virtual uint32_t id() { return const_hash("Shape"); }
};

struct Triangle : public Shape {
  uint32_t id() override { return const_hash("Triangle"); }
};

struct Rectangle : public Shape {
  uint32_t id() override { return const_hash("Rectangle"); }
};

struct Pentagon : public Shape {
  uint32_t id() override { return const_hash("Pentagon"); }
};

void checkShape3(Shape* shape) {
  uint32_t id = shape->id();
  switch (id) {
    case Shape::const_hash("Triangle"): cout << "This is a triangle\n"; break;
    case Shape::const_hash("Rectangle"): cout << "This is a Rectangle\n"; break;
    case Shape::const_hash("Pentagon"): cout << "This is a Pentagon\n"; break;
    default: cout << "None of them"; break;
  }
}




결론적으로 checkShape3 function에서는 모든 값이 compile time에 결정되어 고정값으로 변환되어 컴파일이 되기 때문에 dynamic_cast를 사용하기 혹은 typeid를 사용하기보다 constexpr 함수를 사용하는 것이 속도상으로도 많은 이득이 발생합니다.




[download]

main.cpp