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.