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
推导为 char
。 sizeof(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,因为任何地方都不会出现替换失败。
我使用基于 SFINAE 的方法已经有一段时间了,尤其是通过 std::enable_if
.
因此,我在阅读描述提议的 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
推导为 char
。 sizeof(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,因为任何地方都不会出现替换失败。