2012-08-08 17 views
5

C++ FAQ 35,16¿Por qué la función de miembro amigo no se reconoce como plantilla de función automáticamente?

http://www.parashift.com/c++-faq-lite/template-friends.html

#include <iostream> 

template<typename T> 
class Foo { 
public: 
    Foo(T const& value = T()); 
    friend Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs); 
    friend std::ostream& operator<< (std::ostream& o, const Foo<T>& x); 
private: 
    T value_; 
}; 

Las reclamaciones Autor:

'El inconveniente que ocurre cuando el compilador ve las líneas amigo hacia arriba en la definición de clase adecuada. En ese momento, aún no sabe que las funciones de amigo son en sí mismas plantillas (¿por qué las funciones miembro de la plantilla de clase no son plantilla de función por defecto?); se supone que son los no-templates de esta manera:'

Foo<int> operator+ (const Foo<int>& lhs, const Foo<int>& rhs) 
{ ... } 

std::ostream& operator<< (std::ostream& o, const Foo<int>& x) 
{ ... } 

¿Por qué los que no son plantillas anteriores? ¿Estas plantillas no son instanciadas a través de int?

'Cuando se llama a los < < funciones de operador + o del operador, este supuesto hace que el compilador para generar una llamada a las funciones que no son de plantilla, pero el enlazador le dará un error 'indefinido externa' porque nunca se define realmente esas funciones sin plantilla. '

De hecho, para hacer compilador reconoce lo anterior como plantilla de función, el programador tiene que hacer esto de manera explícita, como a continuación:

template<typename T> class Foo; // pre-declare the template class itself 
template<typename T> Foo<T> operator+ (const Foo<T>& lhs, const Foo<T>& rhs); 
template<typename T> std::ostream& operator<< (std::ostream& o, const Foo<T>& x); 

Podría alguien explicar? Encuentro esto bastante molesto y no sé por qué el compilador no solo crea una instancia de Class Foo reemplazando T por 'int', y lo llama un día.

Gracias.

Respuesta

5

Las funciones de miembros de la plantilla de clase son parte de la plantilla y, por lo tanto, se crean instancias con la plantilla, pero los amigos no. Considere el caso no molde:

struct S { 
    friend void foo(S); 
}; 

Tenga en cuenta que void foo(S) no tiene que ser declarado en este punto; la declaración friend dice que si una función void foo(S) se define, entonces esa función tendrá acceso a S. Puede que nunca se defina realmente, y eso está bien.

Con las plantillas, la situación es la misma:

template<typename T> struct S { 
    friend void foo(S); 
}; 

Esto es decir que para cualquier tipo de T, si una función void foo(S<T>) se define entonces que la función tiene acceso a S<T>. Se espera que la función de ser una función concreta, por la sobrecarga:

void foo(S<char>) { } 
void foo(S<int>) { } 

El compilador no sabe que usted está planeando más adelante para suministrar una plantilla de función que se puede utilizar para todos T. En cambio, si ya se ha declarado una plantilla de función apropiada, se creará una instancia si se especifica que debe agregarle corchetes angulares.

En cuanto a por qué tiene que reenviar-declarar la plantilla, no hay razón para que "la plantilla" tenga una sola declaración.Consideremos:

#include <iostream> 
template<typename T> struct S; 
template<typename T> void foo(S<T>); 
template<typename T> void foo(S<T *>); 
template<typename T> struct S { 
    friend void foo<>(S); 
}; 
template<typename T> void foo(S<T>) { std::cout << "template template friend\n"; } 
template<typename T> void foo(S<T *>) { std::cout << "template specialization template friend\n"; } 
template void foo(S<void *>); 
int main() { 
    foo(S<int>()); 
    foo(S<void *>()); 
} 

Aquí hay dos especializaciones de foo, y tienen que ser declaradas tanto hacia adelante para que el friend puede seleccionar entre ellos.

+0

Entiendo algunas, pero no todas. Con 'amigo Foo operador + (const Foo & lhs, const Foo & rhs);' a lo largo, lo que compilará? ese operador + será una función amiga de Foo, y nada más en el tipo de retorno, parámetro de función, etc., hasta un avance declaración del operador + se encuentra? – user1559625

+0

@ user1559625 el compilador ve, para cualquier 'T' particular, que 'Foo operador + (const Foo & lhs, const Foo & rhs);' será un amigo si está definido. Además, si ya se ha declarado una plantilla adecuada, luego se creará una instancia. – ecatmur

+0

Corrección; la sintaxis es diferente para especificar una plantilla para creación de instancias (los corchetes angulares adicionales). – ecatmur

Cuestiones relacionadas