Using C ++ 11 std :: enable_if to include a member function if the vector is a specific length

I am writing a simple vector class, and I would like to have some member functions that are only available in vectors of a certain length (for example, cross-product for a vector of 3 elements). I stumbled upon std :: enable_if, and it looks like it can do what I want, but it looks like I can't get it to work correctly.

#include <iostream> #include <type_traits> template<typename T, unsigned int L> class Vector { private: T data[L]; public: Vector<T,L>(void) { for(unsigned int i = 0; i < L; i++) { data[i] = 0; } } T operator()(const unsigned int i) const { return data[i]; } T& operator()(const unsigned int i) { return data[i]; } Vector<typename std::enable_if<L==3, T>::type, L> cross(const Vector<T,L>& 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; } }; int main(void) { Vector<double,3> v1; Vector<double,3> v2; Vector<double,3> v3; //Vector<double,4> v4; v1(0) = 1; v1(1) = 2; v1(2) = 3; v2(0) = 4; v2(1) = 5; v2(2) = 6; v3 = v1.cross(v2); std::cout << v3(0) << std::endl; std::cout << v3(1) << std::endl; std::cout << v3(2) << std::endl; return 0; } 

The above code compiles and runs correctly, however, if I uncomment the Vector<double,4> v4 declaration, I get the following compilation error:

 vec.cpp: In instantiation of 'class Vector<double, 4u>': vec.cpp:46:22: required from here vec.cpp:29:59: error: no type named 'type' in 'struct std::enable_if<false, double>' 

Can anyone point out where I'm wrong?

+8
c ++ enable-if
source share
1 answer
  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)

+8
source share

All Articles