I'm currently writing a generic vector template class (the geometric entity, not the container) with the following signature...
template< typename T, unsigned N >
class vector
{...}
... where T is an arithmetic type and N, the dimension. I would like to define the cross product as an overload of operator ^ (located inside class definition) and enable it only when N == 3. What I've have now is:
typename boost::lazy_enable_if_c< (N == 3), vector >::type
inline operator ^(const vector &rhs) const
{
vector ret;
ret(0) = val_[1] * rhs(2) - val_[2] * rhs(1);
ret(1) = val_[2] * rhs(0) - val_[0] * rhs(2);
ret(2) = val_[0] * rhs(1) - val_[1] * rhs(0);
return ret;
}
Unfortunately, instantiating this template with N != 3, even though operator ^ isn't referenced, yields the following error:
error: no type named ‘type’ in ‘struct boost::lazy_enable_if_c < false, flare::math::vector < flare::math::fixed < short int, 8u >, 2u > >’
What am I doing wrong? Is there an alternative to boost::enable_if in such case?
Thank you very much.
The proximal cause of the error message is that, according to the docs, "The second argument of lazy_enable_if
must be a class type that defines a nested type named type
whenever the first parameter (the condition) is true." That's clearly not satisfied here (unless your vector
type just happens to contain typedef something type;
).
You don't need lazy_...
here. According to the docs, that's only needed if the 2nd arg could be undefined (e.g. if the 2nd arg was typename foo<T>::bar
, and the bar
type is not defined for all types T
). vector
(which here means vector<T, N>
) will always be defined.
So definitely try getting rid of lazy_
, or alternatively create a do-nothing traits class template <typename T> struct nop { typedef T type; };
and replace the 2nd arg to lazy_enable_if_c
with nop<vector>
. But my guess is you've already tried the former at least. :)
And now I see why that won't work. According to the standard 14.7.1/1:
Unless a class template specialization has been explicitly instantiated (14.7.2) or explicitly specialized (14.7.3), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program. The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, static data members and member templates;
So anything that causes the class to be instantiated will try to instantiate declarations for all methods, which will fail when N != 3
. So it looks like you'll need to use an always-present method that hands off to a function template instead. Don't worry, any decent compiler will still be able to inline through this:
template< typename T, unsigned N > class vector; // Fwd decl.
template< typename T, unsigned N >
inline boost::enable_if_c< (N == 3), vector<T, N> >::type
magic(const vector<T, N>& lhs, const vector<T, N>& rhs) {
/* Do the calculation as before... */
return ret;
}
template< typename T, unsigned N >
class vector {
...
inline vector operator ^(const vector &rhs) const {
return magic(*this, rhs);
}
};
This will work because the member function definitions are not instantiated unless they are actually called (or their addresses are taken etc.).
I believe your problem is "located inside class definition". I think you'd have less trouble if you overload the operator via a function rather than via a method.
I think it may also be possible to get by with just plain old specialization rather than boost magic once you switch to a function, but I'm less sure about that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With