2010-10-21 32 views
8

¿Qué hay de malo en mi código?C++ plantilla amigo operador sobrecarga

template<int E, int F> 
class Float 
{ 
friend Float<E, F> operator+ (const Float<E, F> &lhs, const Float<E, F> &rhs); 
}; 

G ++ sólo mantiene la advertencia:

float.h:7: warning: friend declaration ‘Float<E, F> operator+(const Float<E, F>&, const Float<E, F>&)’ declares a non-template function

float.h:7: warning: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) -Wno-non-template-friend disables this warning

me trató de add <> after the function name here como se menciona en la nota de advertencia, pero g ++ me da un error.

He compilado el código con clang ++, estaba bien, sin advertencia alguna.

+3

Es posible que desee leer http://www.parashift.com/c++-faq-lite/templates.html#faq-35.16 – UncleBens

Respuesta

24

Es solo una advertencia sobre un aspecto complicado del idioma. Cuando declara una función friend, no es miembro de la clase en la que se encuentra la declaración. Puede definirla allí por conveniencia, pero en realidad pertenece al espacio de nombres.

Al declarar una función amiga que no es una plantilla, dentro de una plantilla de clase, todavía se declara una función que no es de plantilla en el espacio de nombres. No es un miembro de la clase ni una plantilla. Sin embargo, es generado por la plantilla de clase.

Generar funciones que no sean de plantilla desde una plantilla es un poco brumoso. Por ejemplo, no puede agregar una declaración para esa función fuera del bloque class. Por lo tanto, debe definirlo también dentro del bloque class, lo cual tiene sentido porque la plantilla de clase lo generará.

Otra cosa difícil acerca de los amigos es que la declaración dentro de class Float {} no declara la función en el espacio de nombres. Solo puede encontrarlo a través de la resolución de sobrecarga de significado dependiente del argumento, es decir, especificando que un argumento tiene el tipo Float (o una referencia o puntero). Esto no es un problema para operator+, ya que es probable que se sobrecargue de todos modos, y nunca se llamará a excepción de con tipos definidos por el usuario.

Para un ejemplo de un posible problema, imagine que tiene un constructor de conversión Float::Float(Bignum const&). Pero Bignum no tiene operator+. (Disculpe, ejemplo inventado.) Usted desea confiar en operator+(Float const&, Float const&) para Bignum además. Ahora my_bignum + 3 no se compilará porque ninguno de los operandos es Float por lo que no puede encontrar la función friend.

Probablemente, no tiene nada de qué preocuparse, siempre que la función en cuestión sea operator.

O bien, puede cambiar el friend para que sea una plantilla también. En ese caso, debe definirse fuera de el bloque class {}, y declararse antes, en lugar de necesitar declararse y definirse dentro de.

template<int E, int F> // now this is a template! 
Float<E, F> operator+ (const Float<E, F> &lhs, const Float<E, F> &rhs); 

template<int E, int F> 
class Float 
{ 
    // deduce arguments E and F - this names operator+< E, F >. 
friend Float<E, F> operator+<> (const Float<E, F> &lhs, const Float<E, F> &rhs); 
}; 
+1

Gracias por su amable respuesta. –

1

que tiene que hacer exactamente como dicen las advertencias:

template<int E, int F> 
Float<E, F> operator+ (const Float<E, F> &lhs, const Float<E, F> &rhs); 

template<int E, int F> 
class Float 
{ 
friend Float<E, F> operator+<> (const Float<E, F> &lhs, const Float<E, F> &rhs); 
}; 

Esto declara una especialización completa de la plantilla operador de un amigo de una instancia específica de la plantilla de clase. En un comentario a la pregunta UncleBens ha proporcionado amablemente a link to an explanation por qué es tan complicado.

+0

@Potatoswatter: La declaración amigo declara una especialización específica a un amigo de la clase, ¿no? – sbi

+0

En realidad, acabo de probar el código de @ sbi y funciona con mi g ++. –

+0

@Zifei: Este fragmento funciona, pero requiere cambiar la definición de la función. (Lo siento, no noté el cambio en el formulario de la función.) – Potatoswatter

3

Este es un tema bastante antiguo, pero creo que la manera más fácil de declarar al operador es definirlo dentro de la clase Float.

template<int E, int F> 
class Float 
{ 
public: 
    friend Float operator+ (const Float &lhs, const Float &rhs) 
    { 
     // Whatever you need to do. 
    } 
}; 

La sintaxis es más fácil escribir y entender y funcionará exactamente el mismo (excepto que va a ser inline), que no sabe ser una función miembro.

MSDN: Las funciones de amigo definidas dentro de las declaraciones de clase no se consideran dentro del alcance de la clase adjunta; están en el alcance del archivo.

+0

No, en el caso de 'operator +', generalmente la mejor y más fácil forma es implementar 'operator + =' como miembro, y luego implementar 'operator +' como no-miembro no miembro en términos de '+ = '. Esto garantiza que tenga ambos operadores definidos y que se comporten de forma coherente. –