如何在另一个类模板中定义完全专用类的构造函数

How to define constructor of fully-specialized class within another class template

本文关键字:专用 构造函数 定义 另一个      更新时间:2023-10-16

我有一个包含另一个类模板的类模板,并且内部模板具有明确的专用化:

template <typename Outer>
struct ContainingClass {
template <typename T>
struct Rule {
Rule(T value);
// ... other members ...
};
template <>
struct Rule<void> {
Rule();
// ... different members than the non-void Rule<T> ...
};
};

我已经为泛型和专用Rule定义了构造函数:

template <typename Outer>
template <typename T>
ContainingClass<Outer>::Rule<T>::Rule(T value) { }
template <typename Outer>
ContainingClass<Outer>::Rule<void>::Rule() { }

但是 Clang 不喜欢专用类的构造函数:

error: nested name specifier 'ContainingClass<Outer>::Rule<void>::' for declaration does not refer into a class, class template or class template partial specialization
ContainingClass<Outer>::Rule<void>::Rule() { }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^

我对此感到困惑,因为它"引用"ContainingClass<Outer>这是一个类(ContainingClass类模板的实例)。 我怀疑我需要为此更改语法中的某些内容,但不清楚是什么。 如何定义此构造函数?

(如果我删除ContainingClass并将Rule放在命名空间范围内,它可以工作,但我需要在Rule中具有其他取决于Outer类型的东西。 我可以给Rule它自己的Outer模板参数,但这会使使用此类的代码更加尴尬,所以如果可能的话,我想避免使用它。 我知道我可以在Rule类主体中内联定义构造函数,但我想了解为什么单独的定义不起作用。

为了以防万一,我在 Ubuntu 19.04 中使用了 Clang 8.0,在 Mac 上使用 Apple 的"clang-1001.0.46.4"。 (我也尝试过 Ubuntu 的 GCC 8.3,但由于 GCC 错误 #85282 —struct Rule<void>本身定义的"非命名空间范围内的显式专用化",这在不同的地方失败了。

编辑澄清:我的错误不是关于ContainingClass内有template <> struct Rule<void>专业化。 这是 C++14 限制(缺陷 CWG 727)的主题,通过添加虚拟模板参数来解决,因此模板只是部分而不是完全专用的。 我相信限制已经在 C++17 中解除,并且Rule类本身的专业化在 Clang 中运行良好(尽管 GCC 有错误)。 所以我认为虚拟参数解决方法在这里不是正确的解决方案——但如果我弄错了,请告诉我,并且在 C++17 中仍然存在限制。

不能在命名空间范围内声明模板成员的专用化成员。

若要避免此限制,可以使用 c++14 及更早版本以前需要的解决方法,其中包括使用部分专用化而不是完全专用化:

template <typename Outer>
struct ContainingClass {
template <typename T,class=void>
struct Rule {
Rule(T value);
// ... other members ...
};
template <class U>
struct Rule<void,U> {
Rule();
// ... different members than the non-void Rule<T> ...
};
};
template <typename Outer>
template <typename T, typename U>
ContainingClass<Outer>::Rule<T,U>::Rule(T value) { }
template <typename Outer>
template <typename U>
ContainingClass<Outer>::Rule<void,U>::Rule() { }

在 C++17 中,仍然无法在命名空间范围内声明类模板的专用化成员(成员),请参阅 [temp.expl.spec]/17。C++14标准中也存在相同的段落。

C++17 的变化是,我们可以在封闭类模板定义中声明成员的专用化:

  • C++14/[templ.expl.spec]/2

应在包含专用模板的命名空间中声明显式专用化。...]

  • C++17/[templ.expl.spec]/2

可以在可以定义相应主模板的任何范围内声明显式专用化。...]