【问题标题】:How to define constructor of fully-specialized class within another class template如何在另一个类模板中定义完全专业化类的构造函数
【发布时间】:2019-07-04 17:37:15
【问题描述】:

我有一个包含另一个类模板的类模板,而内部模板有一个明确的特化:

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&lt;Outer&gt;,它一个类(ContainingClass 类模板的一个实例)。我怀疑我需要为此更改语法中的某些内容,但不清楚是什么。如何定义这个构造函数?

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

以防万一,我在 Ubuntu 19.04 中使用 Clang 8.0,在 Mac 上使用 Apple 的“clang-1001.0.46.4”。 (我也尝试过 Ubuntu 的 GCC 8.3,但由于 GCC bug #85282 导致在不同的地方失败——struct Rule&lt;void&gt; 本身的定义中的“非命名空间范围内的显式专业化”。)

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

【问题讨论】:

  • @krisz,该限制存在于 C++14 中,但已在 C++17 中解除,这不是我得到的错误。 (由于我提到的错误,在 GCC 中除外。)

标签: c++ templates language-lawyer c++17


【解决方案1】:

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

为避免此限制,您可以使用 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。同一段存在in the C++14 standard

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

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

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

【讨论】:

  • 与 C++14 的限制(和 GCC 错误)非常相似,即显式特化必须在命名空间范围内,而不是在类范围内。所以即使在 17 岁也完全不允许这样做?
  • 感谢您添加的参考资料——我现在看到了区别。
猜你喜欢
  • 2019-10-17
  • 1970-01-01
  • 2015-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-18
  • 2018-08-25
  • 1970-01-01
相关资源
最近更新 更多