4

I have a template function to give me a unique id based on the typename passed to it, like this:

template<typename T>
inline std::size_t get_component_type_id() noexcept
{
    static_assert(std::is_base_of<Component, T>::value, "T must be of type Component.");

    static size_t uniqueComponentId{__INTERNAL__::getUniqueComponentId()};

    return uniqueComponentId;
}

When I call get_component_type_id with BaseClass 10 times, I get the same id. That works perfectly.

However, I want to get the same id as well if I pass a child class to that function. When I call it with ChildClass, I get a different id. Why is that?

Daniel Ribeiro
  • 10,156
  • 12
  • 47
  • 79
  • Possible duplicate of [What should happen to template class static member variables with definition in the .h file](http://stackoverflow.com/questions/7108914/what-should-happen-to-template-class-static-member-variables-with-definition-in) – kfsone Jun 27 '16 at 17:37

3 Answers3

5

This is because an instantiation of a template, once instantiated, has nothing to do with a second instantiation of the same template. The two are seperate entities and get their own static variable.

PS: Here is a video where this occurs in an example: CppCon 2015: Arthur O'Dwyer “Lambdas from First Principles: A Whirlwind Tour of C++"”. The example starts aroung 6:00

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
4

You can try adding a function that calls get_component_type_id() with Component as the template argument when the actual T is a child of Component.

template<class T>
auto fn() noexcept
{
    using type = std::conditional_t<std::is_base_of<Component, T>::value, Component, T>;
    return get_component_type_id<type>();
}
user2296177
  • 2,807
  • 1
  • 15
  • 26
2

get_component_type_id<BaseClass> and get_component_type_id<ChildClass> are two different functions. Hence, you get two of static size_t uniqueComponentId, each with their own value.

Update, in response to comment by OP

Yes, it is possible. You could use:

template <typename T>
inline std::size_t get_component_type_id(T*, std::false_type) noexcept
{
   static size_t uniqueComponentId{__INTERNAL__::getUniqueComponentId()};
   return uniqueComponentId;
}

inline std::size_t get_component_type_id(BaseClass*, std::true_type) noexcept
{
   static size_t uniqueComponentId{__INTERNAL__::getUniqueComponentId()};
   return uniqueComponentId;
}

template<typename T>
inline std::size_t get_component_type_id() noexcept
{
   static_assert(std::is_base_of<Component, T>::value, "T must be of type Component.");
   return get_component_type_id((T*)nullptr, typename std::is_convertible<T, BaseClass>::type{});
}

However, it is fragile. If you want the same behavior for another class derived from Component, you will need to make substantial changes.

You will be better off using a virtual member function.

struct Component
{
   virtual size_t get_type_id() const = 0;
};

struct BaseClass : Component
{
   size_t get_type_id() const
   {
       static size_t uniqueComponentId{__INTERNAL__::getUniqueComponentId()};
       return uniqueComponentId;
   }
};

struct ChildClass : BaseClass {};

Now you can implement size_t get_type_id() const at any level in the inheritance hierarchy as you see fit.

R Sahu
  • 204,454
  • 14
  • 159
  • 270