Wyz*_*a-- 6 c++ templates language-lawyer c++17
我有一个包含另一个类模板的类模板,而内部模板具有显式的专业化:
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> ...
};
};
Run Code Online (Sandbox Code Playgroud)
我已经定义了泛型和专用的构造函数Rule:
template <typename Outer>
template <typename T>
ContainingClass<Outer>::Rule<T>::Rule(T value) { }
template <typename Outer>
ContainingClass<Outer>::Rule<void>::Rule() { }
Run Code Online (Sandbox Code Playgroud)
但是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() { }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
Run Code Online (Sandbox Code Playgroud)
我被这个困惑,因为它“是指进入” 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>自身的定义上“在非命名空间范围内显式专门化”)而在其他地方失败了。)
编辑澄清: 我的错误不是关于内的template <> struct Rule<void>专业化ContainingClass。这是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() { }
Run Code Online (Sandbox Code Playgroud)
在C ++ 17中,仍然不可能在名称空间范围内声明类模板的特殊化成员(的成员),请参见[temp.expl.spec] / 17。C ++ 14标准中存在相同的段落。
C ++ 17的变化是,我们可以在封闭的类模板定义中声明成员的特殊化:
显式专门化应在包含专门化模板的命名空间中声明。[...]
可以在可以定义相应主模板的任何范围内声明显式专业化。[...]