Reputation: 111
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?
Upvotes: 11
Views: 2715
Reputation: 111
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.
Upvotes: 0
Reputation: 61009
You use Base<ProductX<SYNC>, SYNC>
as the members specialization in the definition of s_map
, so you actually need a corresponding partial specialization of Base
(§14.5.5.3/1). In other words, you're trying to define a member of a non-existent partial specialization.
Try providing that specialization:
template<typename SYNC>
struct ProductX;
//-- CRTP base class with some sync/lock mechanism
template<typename T, typename SYNC>
struct Base {};
template<typename SYNC>
struct Base<ProductX<SYNC>, SYNC> {
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" }
};
Demo.
Upvotes: 9
Reputation: 120079
A simplified example.
template <class A, class B>
struct C
{
static int x;
};
template <class A, class B> int C<A, B>::x = 0; // this works
However
template <class A> int C<A, double>::x = 0; // same error as yours
The latter definition belongs to a partial specialization of C which does not exists. Create one:
template <class A>
struct C<A, double>
{
static int x;
};
template <class A> int C<A, double>::x = 1;
and all is well again.
Upvotes: 6