Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent instantiation of a C++ template class method when a specific condition is met?

I'm currently writing a generic vector template class (the geometric entity, not the container) with the following signature...


template&lt typename T, unsigned N &gt
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&lt (N == 3), vector &gt::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 &lt false, flare::math::vector &lt flare::math::fixed &lt short int, 8u &gt, 2u &gt &gt’

What am I doing wrong? Is there an alternative to boost::enable_if in such case?

Thank you very much.

like image 387
pmjobin Avatar asked Nov 28 '10 06:11

pmjobin


2 Answers

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.).

like image 137
j_random_hacker Avatar answered Dec 11 '22 10:12

j_random_hacker


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.

like image 41
Laurence Gonsalves Avatar answered Dec 11 '22 09:12

Laurence Gonsalves