Search code examples
c++templatesgeneric-programmingcrtp

Template definition of non-template error


I want to use the CRTP pattern in combination with some locking mechanism for access syncing in multithreaded environment.

My code looks like this:

//-- CRTP base class with some sync/lock mechanism
template<typename T, typename SYNC>
struct Base {
  static std::unordered_map<int, std::string> s_map;
  static SYNC s_sync;
};

//-- derived class using CRTP
template<typename SYNC>
struct ProductX : public Base<ProductX<SYNC>, SYNC> {};

//-- static initialisation
template<typename SYNC>
std::unordered_map<int, std::string> Base<ProductX<SYNC>, SYNC>::s_map {
  { 1, "value_1" },
  { 2, "value_2" }
}

However I get

error: template definition of non-template std::unordered_map<int, std::basic_string<char> > Base<ProductX<SYNC>, SYNC>::s_map

when compiling.

The error is raised for the static s_map initialisation. Can someone point me what I'm doing wrong?


Solution

  • C++ allows this:

    template<typename SYNC>
    std::unordered_map<int, std::string> Base<ProductX<SYNC>, SYNC>::s_map { };
    

    only with corresponding partial template class specialisation. To do these, please check out responses of Columbo and n.m. users below. However, disadvantage is that you have to re-define everything for every ProductX class you create this way. Ie. in my case, if I want to create classes ProductX, ProductY, ProductZ, I will have to define partial specialisation for each one of them, including all member functions etc, which is not very practical IMHO.

    In case we don't want to write whole class specialisation, we have to use either static variable with no-spec template definition:

    template<typename T, typename SYNC>
    std::unordered_map<int, std::string> Base<T, SYNC>::s_map { };
    

    or fully specialised template definition:

    struct NoSync { };
    template<typename NoSync>
    std::unordered_map<int, std::string> Base<ProductX<NoSync>, NoSync>::s_map { };
    

    Here is full example with full template specialisation:

    //-- CRTP base class with some sync/lock mechanism
    template<typename T, typename SYNC>
    struct Base {
      static std::unordered_map<int, std::string> s_map;
      static SYNC s_sync;
      static std::string& value_name1(int value) { return s_map[value]; }
    };
    
    //-- derived class using CRTP
    template<typename SYNC>
    struct ProductX : public Base<ProductX<SYNC>, SYNC> {};
    
    struct NoSync {};
    
    //-- static initialisation
    template<>
    std::unordered_map<int, std::string> Base<ProductX<NoSync>, NoSync>::s_map {
      { 1, "value_1" },
      { 2, "value_2" }
    };
    
    int main() {
      ProductX<NoSync> p;
      std::cout << "Value: " << p.s_map[1] << "\n";
      std::cout << "Value: " << p.value_name1(2) << "\n";
    }
    

    This one will compile fine.

    I'd like to thank Columbo and 'n.m.' for their replies and for pointing me in right direction! I would select your answers, but I wanted to show this solution without writing class template specialisation.