template<unsigned LL = L> Vector<typename std::enable_if<LL==3 && L == 3, T>::type, LL> cross(const Vector<T,LL>& vec2) const { Vector<T,L> result; result(0) = (*this)(1) * vec2(2) - (*this)(2) * vec2(1); result(1) = (*this)(2) * vec2(0) - (*this)(0) * vec2(2); result(2) = (*this)(0) * vec2(1) - (*this)(1) * vec2(0); return result; }
PS. Why does it work this way?
The definition of the variable v4 causes the implicit creation of a template of the Vector class, which in turn causes the implicit creation of declarations of member functions of the class (14.7.1. Implicit creation of an instance [temp.inst] # 1). This last instance, of course, leads to an error.
If we instead replace the member function as a member template, in accordance with the same sentence, at this moment the member template itself is created , and this creation looks more or less, for example:
template<unsigned LL = 3> Vector<typename std::enable_if<LL==3 && 3 == 3, double>::type, LL> cross(const Vector<double,LL>& vec2) const;
which is a fully valid template declaration. We cannot (and cannot) do any further creation at this point.
However, when we try to actually call cross , this is without a doubt a “context that requires a member / function definition”, therefore according to (14.7.1 Implicit instantiation [temp.inst] # 2, # 3), the cross ( the second cross template, the one that is the result of creating an instance of the outer class) is implicitly created, and std::enable_if given the opportunity to do its job. As a side note, this is where the SFINAE principle applies.
SFC. To develop a little further, although it is not directly related to the OP issue, it is worth mentioning that it is not always necessary to declare members as templates for handling such situations.
There are situations when a class template member is not "valid" for a given instance, but still a template can be created, for example:
#include <type_traits> template<typename T> struct S { T x; T foo () const { return x; } typename std::remove_pointer<T>::type bar () const { return *x; } }; S<int> x; S<int *> y;
Apparently, in the instance S<int> expression *x is not valid, because the type x is int . However, this program is correct. The important point is that during an implicit instance only member declarations are created. In the above case, an instance of S<int> creates an instance int bar() const; which is a completely correct ad.
Of course, if later we try to create an instance of the definition of S<int>::bar , as in:
void f() { x.foo (); // x.bar (); // error y.foo (); y.bar (); }
we will get an error message.
(This still follows from the above two paragraphs of the C ++ standard)