非模板错误的模板定义

Template definition of non-template error

我想将 CRTP pattern 与一些锁定机制结合使用以在多线程环境中进行访问同步。

我的代码如下所示:

//-- 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" }
}

不过我得到

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

编译时。

静态 s_map 初始化引发错误。有人可以指出我做错了什么吗?

您在 s_map 的定义中使用 Base<ProductX<SYNC>, SYNC> 作为成员特化,因此您实际上需要 Base 的相应部分特化 (§14.5.5.3/1)。换句话说,您正在尝试定义一个不存在的偏特化的成员。

尝试提供该专业:

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.

一个简化的例子。

template <class A, class B>
struct C
{
    static int x;
};

template <class A, class B> int C<A, B>::x = 0; // this works

但是

template <class A> int C<A, double>::x = 0; // same error as yours

后一个定义属于不存在的 C 的偏特化。创建一个:

template <class A>
struct C<A, double>
{
    static int x;
};

template <class A> int C<A, double>::x = 1;

一切都好起来了。

C++ 允许这样做:

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

仅适用于相应的部分模板 class 专业化。为此,请查看 Columbo 和 n.m 的回复。下面的用户。然而,缺点是您必须为您以这种方式创建的每个 ProductX class 重新定义所有内容。 IE。在我的例子中,如果我想创建 classes ProductXProductYProductZ,我将不得不为它们中的每一个定义偏特化,包括所有成员函数等等,恕我直言,这不是很实用。

如果我们不想编写整个 class 专业化,我们必须使用没有规范模板定义的静态变量:

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

或完全专业化的模板定义:

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

这是具有完整模板专业化的完整示例:

//-- 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";
}

这个可以正常编译。

我要感谢 Columbo 和 'n.m.' 的回复并为我指明了正确的方向!我会 select 你的答案,但我想在不编写 class 模板专业化的情况下展示这个解决方案。