2

I would like to specialize getVector member function, i'm trying to use SFINAE for this. But it works only if Dim is 3 or greater.

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>'
Yola
  • 18,496
  • 11
  • 65
  • 106
  • have you tried placing the "Dim>x" checks as the last && term ? I'm not sure the compiler is right, or if it's just implementation defined, but it may shortcircuit the enable_if condition and consider its value non-dependent on VDim, hence giving a compiler error during member declaration instantiation (that is, at Mat<2> instantation point) – Massimiliano Janes Dec 27 '17 at 11:43
  • There's something you're not showing us. Member functions of templated classes aren't instantiated if they're not used. Could you show us the calling site? – papagaga Dec 27 '17 at 11:50
  • 4
    @papagaga that's not entirely correct, member function declarations (but not their definitions) are instantiated when the class template is. So, if the declaration contains an expressions that's value-dependent *only* on the class template parameter, and that expression results ill formed, an error will be raised even if the function is not "used"... – Massimiliano Janes Dec 27 '17 at 11:56
  • Both of them compile fine. https://www.ideone.com/g4rRMu – frogatto Dec 27 '17 at 11:59
  • @MassimilianoJanes: in this case `getVectorBegin` isn't value-dependent on the class template parameter only, is it? But thanks for the precision. / @Hi I'm Frogatto: that's because there's something in the constructor invoking `getVectorBegin` we don't see in the snippet. – papagaga Dec 27 '17 at 12:00
  • @MassimilianoJanes yes, it works this way. You can post it as an answer if you want. Also, some elaboration would be appreciated. Thanks! And thanks once more for your second comment! – Yola Dec 27 '17 at 13:02

1 Answers1

2

This is going to be tricky.

I believe your code is ok for Dim>=2 but it's ill-formed when given a Dim<=1 argument, with no diagnostic required, but for a different reason your compiler is complaining about.


For Dim>=2 it is correct, but your compiler (MSVC++ I guess) complains in the Dim==2 case. Given the error description, I suppose the reason is that it's erroneously short-circuiting the && expressions in the enable_if conditions, interpreting them as value-dependent only on the class template parameter when Dim > x is false. The workaround is to move the Dim > x checks as the last term of the && expression.

To elaborate, the situation is conceptually similar to the following code snippet:

template <size_t N>
class foo
{
    template <typename E = enable_if_t<(N>0)>>
    void bar();
};

foo<1> f1;
foo<0> f0; // fails

here, the instantation of foo triggers the instantiation of the declarations (but not the definitions) of its members (see [temp.inst]#1); but, only checks of names and expressions that depend on the member template parameters are postponed at their respective instantiation points. Here, the type name enable_if_t<(N>0)> does not depend on any bar() template parameter (N belongs to foo's template parameters list) hence is non-dependent, resulting in enable_if<false>::type when N == 0, so the error.


Going back to your code, consider:

[temp.dep.constexpr]#1 Except as described below, a constant expression is value-dependent if any subexpression is value-dependent

and nowhere shortcircuiting operators are mentioned as an exception. So, the expression, say, Dim > 1 && VDim == 0 is value-dependent even if Dim<=1; therefore, no error should occur until substitution of VDim (where SFINAE would apply). In fact, both gcc and clang agree on accepting your code.

That said, when Dim<=1 the first and third getVectorBegin overloads actually declare functionally equivalent member templates (see [temp.over.link]#6), so I believe it's ill-formed in that case.

Massimiliano Janes
  • 5,524
  • 1
  • 10
  • 22