SFINAE 和部分 class 模板专业化

SFINAE and partial class template specializations

我使用基于 SFINAE 的方法已经有一段时间了,尤其是通过 std::enable_if.

enable/disable 特定的 class 模板专业化

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

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

第 4 节专门讨论习语的有效性,并且指的是两方争论 SFINAE 在部分 class 模板专业化中的适用性的讨论(Richard Smith 指出该标准缺少关于此主题的措辞)。在本节末尾,提到了以下 CWG 问题

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

这里再次声明,标准没有明确允许问题中转载的示例。

我有点困惑,因为在我看来,例如 enable_if 在偏特化中的使用已经成为相当长一段时间的标准做法(例如参见 Boost 文档,其中明确提到偏特化专业)。

我是不是误解了上面文档中的要点,还是这真的是一个灰色地带?

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

开始

A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list (14.8.2).

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

If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments. [ Note: If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution process. —end note ] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.

来自 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。我们查看一个偏特化,看看它是否 "matches" - 这意味着模板参数 "can be deduced" - 也就是说:

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

适用正常模板推导规则。第二个 "argument" 是不可推导的上下文,因此我们将 T 推导为 charsizeof(char) > sizeof(float), 为 false, enable_if_t<false, float> 是无效类型,所以类型推导应该失败...但是,推导失败只能发生

in the immediate context of the function type and its template parameter types

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

然而,规则的精神似乎更符合:

Only invalid types and expressions in the immediate context of the deduction process can result in a deduction failure.

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

For two class template partial specializations, the first is more specialized than the second if, given the following rewrite to two function templates, [...]

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


示例本身就是 X<double, float>。但这实际上并没有证明或要求 SFINAE,因为任何地方都不会出现替换失败。