10

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?

Martin Pasko
  • 101
  • 1
  • 7
  • @Deduplicator - this is not a duplicate of what you marked. If duplicate of something, then of this one: http://stackoverflow.com/questions/13404695/c-how-to-initialize-static-variables-of-a-partial-template-specialization – Martin Pasko Jul 24 '15 at 11:44

3 Answers3

9

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.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • Thank you for your answer Columbo! You are right and have pointed me in right direction. However, writing template class specialisation is not very convenient in my case, as I have to rewrite everything for every ProductX I create. See my answer below for more details. – Martin Pasko Jul 22 '15 at 13:01
6

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.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • Thanks for your answer! As Columbo, you have pointed me in right direction. See my own answer below for more details. – Martin Pasko Jul 22 '15 at 13:02
0

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
Martin Pasko
  • 101
  • 1
  • 7