【问题标题】:SFINAE and partial class template specializationsSFINAE 和部分类模板专业化
【发布时间】:2015-08-21 00:18:07
【问题描述】:

我使用基于 SFINAE 的方法已经有一段时间了,尤其是通过std::enable_if 启用/禁用特定的类模板特化。

因此,在阅读描述提议的void_t 别名/检测成语的论文时,我有点困惑:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf

第 4 节专门讨论成语的有效性,并指的是两方争论 SFINAE 在部分类模板专业化中的适用性的讨论(Richard Smith 指出该标准缺少关于这个话题)。在本节的最后,提到了以下 CWG 问题

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2054

这里再次声明该标准并未明确允许该问题中复制的示例。

我有点困惑,因为在我看来,例如,enable_if 在部分专业化中的使用已经成为标准实践已经有一段时间了(例如,参见 Boost 文档,其中明确提到了部分专业化)。

我是否误解了上述文档中的要点,或者这真的是一个灰色区域?

【问题讨论】:

  • 这似乎是一个不错的论点,标准没有指定它,并且每个实现都可以按照您的意愿处理它,并且几乎肯定会以匹配状态的方式解决它现状?
  • @Yakk 但我有点惊讶地看到它被提升为在 Boost 和 stackoverflow 等圈子中做事的可移植方式。这是一种在早期形式中可以追溯到 C++03 的技术,它在 C++11/C++14 中没有被标准化似乎很奇怪。
  • 好吧,可能没人注意到。
  • 我重新阅读了标准的相关部分,这似乎是一个可能有助于明确澄清的案例。问题在于,在关于类模板规范 14.5.5.1/2 的部分中,它基本上说“查看 14.8.2”以了解实际规则。 14.8.2 确实包含了所有 SFINAE 的东西,但那是关于函数模板的部分,而不是类模板。在我看来,他们似乎不想重复自己,因此重定向 - 但听起来确实很笨重。
  • @Yakk 忘了说谢谢你的回复,你说的很有道理:)

标签: c++ templates c++11 c++14


【解决方案1】:

我想争辩说,由于措辞缺陷,该标准不支持部分专业化的 SFINAE。让我们从[temp.class.spec.match]开始:

如果模板参数的模板参数列表,则部分特化匹配给定的实际模板参数列表 部分特化可以从实际的模板参数列表中推导出来 (14.8.2)。

并且,从 [temp.deduct],SFINAE 子句:

如果替换导致无效的类型或表达式,则类型推导失败。无效的类型或表达式是 如果使用替换参数编写,则格式错误,需要诊断。 [ 笔记: 如果不需要诊断,则程序仍然是格式错误的。访问检查是作为替换的一部分完成的 过程。 —end note ] 只有在函数类型及其模板参数类型的直接上下文中的无效类型和表达式会导致推导失败。

CWG 稍作修改的示例 是:

template <class T, class U> struct X   {
    typedef char member;
};

template<class T> struct X<T,
     typename enable_if<(sizeof(T)>sizeof(
 float)), float>::type>
{
    typedef long long member;
};

int main() {
    cout << sizeof(X<char, float>::member);
}

X 上的名称查找找到主要名称,T == char, U == float。我们看一个偏特化,看看它是否“匹配”——这意味着模板参数“可以推导出来”——也就是说:

+-------------+--------+-------------------------------------------------+
|             | arg1     arg2                                            |
+-------------+--------+-------------------------------------------------+
| deduce T in | T      | enable_if_t<(sizeof(T) > sizeof(float), float>  |
| from        | char   | float                                           |
+-------------+--------+-------------------------------------------------+

适用正常的模板扣除规则。第二个“参数”是不可演绎的上下文,因此我们将T 演绎为charsizeof(char) &gt; sizeof(float), 为假,enable_if_t&lt;false, float&gt; 是无效类型,所以类型推导应该失败...但是,只能发生推导失败

函数类型及其模板参数类型的直接上下文中

我们处理的不是函数类型或函数模板参数类型,而是类模板参数类型。类不是函数,因此如果我们从字面上理解所有内容,则不应应用 SFINAE 排除 - 修改后的 CWG 示例会导致硬错误。

但是,规则的精神似乎更符合以下几点:

只有在演绎过程的直接上下文中的无效类型和表达式会导致演绎失败。

我不知道具体排除类部分专业化扣除的原因是什么。此外,类模板部分特化的部分排序也看起来像函数。来自[temp.class.order]:

对于两个类模板部分特化,如果,第一个比第二个更特化,给定 以下重写为两个函数模板,[...]

标准因此已经在下一节展示了类模板部分特化和函数模板之间的二元性。这仅适用于部分专业化排序,而不适用于部分专业化参数推导期间的替换失败,这让我觉得这是一个缺陷。


示例本身是X&lt;double, float&gt;。但这实际上并没有证明或需要 SFINAE,因为在任何地方都不会有替换失败。

【讨论】:

  • 自撰写此答案以来有什么变化吗?
  • @Evg 我不这么认为。替换失败和直接上下文通常没有充分说明。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多