Estaba luchando con el problema descrito en this question (declarando una función de plantilla como amigo de una clase de plantilla), y creo que la segunda respuesta es lo que quiero hacer (reenviar declarar la plantilla función, luego nombre una especialización como amigo). Tengo una pregunta acerca de si una solución ligeramente diferente es realmente correcto o simplemente pasa a trabajar en Visual C++ 2008.Función de amigo de plantilla de una clase de plantilla
Código de ensayo es:
#include <iostream>
// forward declarations
template <typename T>
class test;
template <typename T>
std::ostream& operator<<(std::ostream &out, const test<T> &t);
template <typename T>
class test {
friend std::ostream& operator<< <T>(std::ostream &out, const test<T> &t);
// alternative friend declaration
// template <typename U>
// friend std::ostream& operator<<(std::ostream &out, const test<T> &t);
// rest of class
};
template <typename T>
std::ostream& operator<<(std::ostream &out, const test<T> &t) {
// output function defined here
}
En primer lugar, una cosa extraña que encontré fue que si cambio la declaración directa de operator<<
para que no coincida (por ejemplo, std::ostream& operator<<(std::ostream &out, int fake);
, todo todavía se compila y funciona correctamente (para ser claros, no es necesario definir dicha función, solo declararla). Sin embargo, como en el pregunta vinculada, la eliminación de la declaración directa causa un problema ya que el compilador parece pensar que estoy declarando un miembro de datos en lugar de una función de amigo. Estoy bastante seguro de que este comportamiento es un error de Visual C++ 2008.
Lo interesante es cuando elimino las declaraciones de reenvío y uso la declaración de amigo alternativa en el código anterior. Tenga en cuenta que el parámetro de plantilla U
no aparece en la siguiente firma. Este método también se compila y funciona correctamente (sin cambiar nada más). Mi pregunta es si esto se ajusta al estándar o una idiosincrasia de Visual C++ 2008 (no pude encontrar una buena respuesta en mis libros de referencia).
Tenga en cuenta que mientras que una declaración amigo template <typename U> friend ... const test<U> &t);
también funciona, en realidad esto le da a cada instancia del acceso del operador friend
a cualquier instancia de test
, mientras que lo que quiero es que los miembros privados de test<T>
sólo deben ser accesibles desde operator<< <T>
. Probé esto instanciando un test<int>
dentro del operator<<
y accediendo a un miembro privado; esto debería causar un error de compilación cuando intento generar un test<double>
.
Sinopsis: Eliminar las declaraciones directas y cambiar a la declaración de amigo alternativa en el código anterior parece producir el mismo resultado (en Visual C++ 2008): ¿es este código realmente correcto?
ACTUALIZACIÓN: cualquiera de las modificaciones anteriores al código no funciona en gcc, así que supongo que estos son errores o "características" en el compilador de Visual C++. De todos modos, apreciaría los comentarios de personas familiarizadas con el estándar.
Incidentalmente, descubrí que agregar 'using namespace std;' antes de la declaración de clase eliminará la necesidad de las declaraciones forward, que supongo que es un efecto secundario del error (posible) del compilador. –
Según mi comentario, ¿puede agregar explícitamente los ejemplos (incluida la creación de instancias) que está diciendo que funcionan? Verbalmente es bastante difícil entender lo que quieres decir con cada ejemplo. –