2017-12-27 367 views
2

我想专精getVector成员函数,我正在尝试使用SFINAE。但它只有在Dim为3或更大时才有效。SFINAE模板成员超载

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>' 
+0

您是否尝试将“Dim> x”检查作为最后的&&术语?我不确定编译器是否正确,或者它只是实现定义的,但它可能会缩短enable_if条件,并认为它的值不依赖于VDim,因此在成员声明实例化期间发生编译器错误(即,在Mat <2>瞬间点) –

+0

有些东西你没有向我们展示。如果不使用模板类的成员函数,则不会实例化。你能告诉我们呼叫网站吗? – papagaga

+4

@papagaga这不完全正确,成员函数声明(但不是它们的定义)在类模板时被实例化。因此,如果声明在类模板参数中包含一个仅依赖于值的表达式,并且表达式结果不正确,那么即使该函数未被“使用”也会产生错误... –

回答

2

这将是棘手。

我相信你的代码对于Dim>=2是可以的,但是当给出Dim<=1参数时不合格,不需要诊断,但是由于编译器抱怨的原因不同。


对于Dim>=2它是正确的,但你的编译器(MSVC++我猜)在Dim==2情况下抱怨。鉴于错误描述,我想这是因为Dim > x为false时,它错误地短路了enable_if条件中的表达式,并将它们解释为值类型仅在类模板参数上。解决方法是将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的instantation触发声明的成员的实例化(而不是定义)(见[ temp.inst]#1);但是只有在成员上依赖的名称和表达式的检查才会在它们各自的实例化点被推迟。这里,类型名enable_if_t<(N>0)>确实依赖于任何bar()模板参数(N属于foo的模板参数列表),因此是不相关的,从而导致enable_if<false>::typeN == 0,所以该错误。


再回到您的代码,可以考虑:

[temp.dep.constexpr]#1除了下面所描述的,一个常量表达式是值依赖性如果任何子表达式是取决于数值

并没有提到任何短路运营商作为例外。因此,表达式即Dim > 1 && VDim == 0是值依赖的,即使Dim<=1;因此,在替代VDim(其中SFINAE将适用)之前不应出现错误。实际上,gcc和叮当agree接受你的代码。

这就是说,Dim<=1第一和第三getVectorBegin重载实际声明功能上等同成员模板时(见[temp.over.link]#6),所以我认为这是非法的构造在这种情况下。