SFINAE 关于模板成员重载
SFINAE on template member overloading
我想专门化 getVector
成员函数,我正尝试为此使用 SFINAE。但它仅在 Dim 为 3 或更大时才有效。
template <size_t Dim>
class Mat
{
...
template <size_t VDim, typename enable_if<(Dim > 1 && VDim == 0)>::type* = nullptr>
void getVectorBegin(const array<size_t, Dim - 1>& indexAfter) const;
template <size_t VDim, typename enable_if<(Dim > 2 && 0 < VDim && VDim < Dim-1)>::type* = nullptr>
void getVectorBegin(const array<size_t, VDim>& indexBefore, const array<size_t, Dim - VDim - 1>& indexAfter) const;
template <size_t VDim, typename enable_if<(Dim > 1 && VDim == Dim-1)>::type* = nullptr>
void getVectorBegin(const array<size_t, Dim - 1>& indexBefore) const;
};
Mat<3> m;
Mat<2> m; // error C2039: 'type': is not a member of 'std::enable_if<false,_Ty>'
这会很棘手。
我相信你的代码对于 Dim>=2
是没问题的,但是当给定一个 Dim<=1
参数时它的格式不正确,不需要诊断,但是你的编译器抱怨的原因不同。
对于 Dim>=2
它是正确的,但是您的编译器(我猜是 MSVC++)在 Dim==2
的情况下会抱怨。鉴于错误描述,我想原因是它错误地短路了 enable_if
条件中的 && 表达式,将它们解释为值依赖 only only class 模板参数,当 Dim > x
为假时。解决方法是将 Dim > x
检查移动到 && 表达式的最后一项。
具体来说,这种情况在概念上类似于以下代码片段:
template <size_t N>
class foo
{
template <typename E = enable_if_t<(N>0)>>
void bar();
};
foo<1> f1;
foo<0> f0; // fails
此处,foo
的实例化触发了其成员的声明(但不是定义)的实例化(参见 [temp.inst]#1);但是,只有在 member 模板参数上 depend 的名称和表达式的检查才会在它们各自的实例化点被推迟。这里,类型名称 enable_if_t<(N>0)>
不 依赖于任何 bar()
模板参数(N
属于 foo
的模板参数列表) 因此是非依赖的,导致 enable_if<false>::type
当 N == 0
,所以错误。
回到您的代码,考虑:
[temp.dep.constexpr]#1 Except as described below, a constant expression is value-dependent if any subexpression is value-dependent
并且没有任何地方提到短路运算符作为例外。因此,即使 Dim<=1
,表达式 Dim > 1 && VDim == 0
也是依赖于值的;因此,在替换 VDim
(适用 SFINAE 的地方)之前,不会出现错误。事实上,gcc 和 clang agree 都在接受你的代码。
也就是说,当 Dim<=1
第一个和第三个 getVectorBegin
重载实际上声明 功能等效 成员模板(参见 [temp.over.link]#6), 所以我认为在那种情况下它的格式不正确。
我想专门化 getVector
成员函数,我正尝试为此使用 SFINAE。但它仅在 Dim 为 3 或更大时才有效。
template <size_t Dim>
class Mat
{
...
template <size_t VDim, typename enable_if<(Dim > 1 && VDim == 0)>::type* = nullptr>
void getVectorBegin(const array<size_t, Dim - 1>& indexAfter) const;
template <size_t VDim, typename enable_if<(Dim > 2 && 0 < VDim && VDim < Dim-1)>::type* = nullptr>
void getVectorBegin(const array<size_t, VDim>& indexBefore, const array<size_t, Dim - VDim - 1>& indexAfter) const;
template <size_t VDim, typename enable_if<(Dim > 1 && VDim == Dim-1)>::type* = nullptr>
void getVectorBegin(const array<size_t, Dim - 1>& indexBefore) const;
};
Mat<3> m;
Mat<2> m; // error C2039: 'type': is not a member of 'std::enable_if<false,_Ty>'
这会很棘手。
我相信你的代码对于 Dim>=2
是没问题的,但是当给定一个 Dim<=1
参数时它的格式不正确,不需要诊断,但是你的编译器抱怨的原因不同。
对于 Dim>=2
它是正确的,但是您的编译器(我猜是 MSVC++)在 Dim==2
的情况下会抱怨。鉴于错误描述,我想原因是它错误地短路了 enable_if
条件中的 && 表达式,将它们解释为值依赖 only only class 模板参数,当 Dim > x
为假时。解决方法是将 Dim > x
检查移动到 && 表达式的最后一项。
具体来说,这种情况在概念上类似于以下代码片段:
template <size_t N>
class foo
{
template <typename E = enable_if_t<(N>0)>>
void bar();
};
foo<1> f1;
foo<0> f0; // fails
此处,foo
的实例化触发了其成员的声明(但不是定义)的实例化(参见 [temp.inst]#1);但是,只有在 member 模板参数上 depend 的名称和表达式的检查才会在它们各自的实例化点被推迟。这里,类型名称 enable_if_t<(N>0)>
不 依赖于任何 bar()
模板参数(N
属于 foo
的模板参数列表) 因此是非依赖的,导致 enable_if<false>::type
当 N == 0
,所以错误。
回到您的代码,考虑:
[temp.dep.constexpr]#1 Except as described below, a constant expression is value-dependent if any subexpression is value-dependent
并且没有任何地方提到短路运算符作为例外。因此,即使 Dim<=1
,表达式 Dim > 1 && VDim == 0
也是依赖于值的;因此,在替换 VDim
(适用 SFINAE 的地方)之前,不会出现错误。事实上,gcc 和 clang agree 都在接受你的代码。
也就是说,当 Dim<=1
第一个和第三个 getVectorBegin
重载实际上声明 功能等效 成员模板(参见 [temp.over.link]#6), 所以我认为在那种情况下它的格式不正确。