2010-11-28 6 views
6

Actualmente estoy escribiendo una clase genérica de plantilla vector (la entidad geométrica, no el contenedor) con la siguiente firma ...Cómo evitar la creación de instancias de un método de clase de plantilla C++ cuando se cumple una condición específica?


template< typename T, unsigned N > 
class vector 
{...} 

... donde T es un tipo aritmético y N, la dimensión. Me gustaría definir el producto cruz como una sobrecarga del operador^(ubicado dentro definición de clase) y permitir que sólo cuando N == 3. Lo que he tengo ahora es:


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; 
} 

Por desgracia, crear instancias de esta plantilla ! con N = 3, a pesar de que el operador^no se hace referencia, da el siguiente error:


error: no type named ‘type’ in ‘struct boost::lazy_enable_if_c < false, flare::math::vector < flare::math::fixed < short int, 8u >, 2u > >’ 

¿Qué estoy haciendo mal? ¿Hay una alternativa para impulsar :: enable_if en tal caso?

Muchas gracias.

Respuesta

4

La causa proximal del mensaje de error es que, de acuerdo con the docs, "El segundo argumento de lazy_enable_if debe ser un tipo de clase que define un tipo anidado llamado type cada vez que el primer parámetro (la condición) es verdadero". Claramente, esto no está satisfecho (a menos que su tipo vector contenga typedef something type;).

No necesita lazy_... aquí. De acuerdo con los documentos, eso solo es necesario si el segundo arg podría estar indefinido (por ejemplo, si el segundo arg fue typename foo<T>::bar, y el tipo bar no está definido para todos los tipos T). vector (que aquí significa vector<T, N>) siempre se definirá.

Así que definitivamente tratar de deshacerse de lazy_, o, alternativamente, crear una que no hace nada rasgos de clase template <typename T> struct nop { typedef T type; }; y sustituir la segunda arg a lazy_enable_if_c con nop<vector>. Pero supongo que ya has probado el primero al menos. :)

Y ahora veo por qué eso no va a funcionar. De acuerdo con la norma 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;

Así que cualquier cosa que causa la clase de una instancia va a tratar de crear una instancia de declaraciones para todos los métodos, que fallará cuando N != 3. Por lo tanto, parece que tendrá que usar un método siempre presente que se transfiera a una plantilla de función. No se preocupe, cualquier compilador decente todavía será capaz de inline a través de este:

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); 
    } 
}; 

Esto funciona porque la función de miembro de definiciones no son instanciados a menos que se llama en realidad (o se toman sus direcciones, etc.)

+0

Antes que nada, muchas gracias por la explicación en profundidad.De hecho, tenía un tipo de vector typedef; dentro de mi definición de clase, pero olvidé mencionar este detalle importante. Es por eso que no entendí por qué GCC arrojaba este error sobre :: tipo no definido. Además, no entendí el propósito de la versión lazy_ de enable_if. Tu explicación me ayudó a entenderlo. A la luz de esto, simplemente me encontré con la versión externa del operador^y funcionó como se esperaba. El mensaje de error generado cuando N! = 3 es un poco críptico, pero esto se puede resolver. ¡De nuevo muchas gracias! – pmjobin

+0

@pmjobin: De nada :) Sí, los mensajes de error de la plantilla son terribles ... –

2

Creo que su problema está "ubicado dentro de la definición de la clase". Creo que tendrías menos problemas si sobrecargas al operador a través de una función en lugar de a través de un método.

Creo que también es posible salir adelante con la simple y antigua especialización en lugar de aumentar la magia una vez que cambias a una función, pero estoy menos seguro de eso.

Cuestiones relacionadas