c++ templates operator-overloading friend ostream

c++ - Sobrecarga del operador amigo<< para la clase de plantilla



templates operator-overloading (5)

Aqui tienes:

#include <cstdlib> #include <iostream> using namespace std; template <class T> T my_max(T a, T b) { if(a > b) return a; else return b; } template <class classT> class D { public: D(classT in) : d(in) {}; bool operator>(const D& rhs) const { return d > rhs.d;}; classT operator=(const D<classT>& rhs); template<class classT> friend ostream& operator<< (ostream & os, const D<classT>& rhs); private: classT d; }; template<class classT> ostream& operator<<(ostream& os, class D<typename classT> const& rhs) { os << rhs.d; return os; } int main() { int i1 = 1; int i2 = 2; D<int> d1(i1); D<int> d2(i2); cout << my_max(d1,d2) << endl; return 0; }

He leído un par de preguntas sobre mi problema en stackoverflow ahora, y nada parece resolver mi problema. O tal vez lo he hecho mal ... El << sobrecargado << si lo hago en una función en línea. ¿Pero cómo hago que funcione en mi caso?

warning: friend declaration std::ostream& operator<<(std::ostream&, const D<classT>&)'' declares a non-template function

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

/tmp/cc6VTWdv.o:uppgift4.cc:(.text+0x180): undefined reference to operator<<(std::basic_ostream<char, std::char_traits<char> >&, D<int> const&)'' collect2: ld returned 1 exit status

El código:

template <class T> T my_max(T a, T b) { if(a > b) return a; else return b; } template <class classT> class D { public: D(classT in) : d(in) {}; bool operator>(const D& rhs) const; classT operator=(const D<classT>& rhs); friend ostream& operator<< (ostream & os, const D<classT>& rhs); private: classT d; }; int main() { int i1 = 1; int i2 = 2; D<int> d1(i1); D<int> d2(i2); cout << my_max(d1,d2) << endl; return 0; } template <class classT> ostream& operator<<(ostream &os, const D<classT>& rhs) { os << rhs.d; return os; }


Creo que no deberías hacer amigo en primer lugar.

Puede crear una llamada a método público print, algo como esto (para una clase que no sea de plantilla):

std::ostream& MyClass::print(std::ostream& os) const { os << "Private One" << privateOne_ << endl; os << "Private Two" << privateTwo_ << endl; os.flush(); return os; }

y luego, fuera de la clase (pero en el mismo espacio de nombres)

std::ostream& operator<<(std::ostream& os, const MyClass& myClass) { return myClass.print(os); }

Creo que debería funcionar también para la clase de plantilla, pero todavía no lo he probado.


Esta es una de esas preguntas frecuentes que tienen diferentes enfoques que son similares pero que no son lo mismo. Los tres enfoques difieren en quién declara ser un amigo de su función, y luego en cómo lo implementa.

El extrovertido

Declare todas las instancias de la plantilla como amigos. Esto es lo que ha aceptado como respuesta, y también lo que la mayoría de las otras respuestas proponen. En este enfoque, usted está innecesariamente abriendo su instanciación particular D<T> al declarar amigos a todos los operator<< instanciaciones. Es decir, std::ostream& operator<<( std::ostream &, const D<int>& ) tiene acceso a todas las std::ostream& operator<<( std::ostream &, const D<int>& ) internas de D<double> .

template <typename T> class Test { template <typename U> // all instantiations of this template are my friends friend std::ostream& operator<<( std::ostream&, const Test<U>& ); }; template <typename T> std::ostream& operator<<( std::ostream& o, const Test<T>& ) { // Can access all Test<int>, Test<double>... regardless of what T is }

Los introvertidos

Solo declare una instancia particular del operador de inserción como un amigo. D<int> puede gustar el operador de inserción cuando se aplica a sí mismo, pero no quiere tener nada que ver con std::ostream& operator<<( std::ostream&, const D<double>& ) .

Esto se puede hacer de dos maneras, de la manera más simple, como lo propuso @Emery Berger, que está incrustando al operador, lo que también es una buena idea por otros motivos:

template <typename T> class Test { friend std::ostream& operator<<( std::ostream& o, const Test& t ) { // can access the enclosing Test. If T is int, it cannot access Test<double> } };

En esta primera versión, no está creando un operator<< plantilla operator<< , sino una función sin plantilla para cada instancia de la plantilla de Test . De nuevo, la diferencia es sutil, pero esto es básicamente equivalente a agregar manualmente: std::ostream& operator<<( std::ostream&, const Test<int>& ) cuando crea una instancia de Test<int> , y otra sobrecarga similar cuando crea una instancia Test con double o con cualquier otro tipo.

La tercera versión es más engorrosa. Sin incluir el código, y con el uso de una plantilla, puede declarar una sola instanciación de la plantilla como un amigo de su clase, sin abrirse a todas las demás instancias:

// Forward declare both templates: template <typename T> class Test; template <typename T> std::ostream& operator<<( std::ostream&, const Test<T>& ); // Declare the actual templates: template <typename T> class Test { friend std::ostream& operator<< <T>( std::ostream&, const Test<T>& ); }; // Implement the operator template <typename T> std::ostream& operator<<( std::ostream& o, const Test<T>& t ) { // Can only access Test<T> for the same T as is instantiating, that is: // if T is int, this template cannot access Test<double>, Test<char> ... }

Aprovechando el extrovertido

La sutil diferencia entre esta tercera opción y la primera es en cuánto estás abriendo a otras clases. Un ejemplo de abuso en la versión extrovertida sería alguien que quiere tener acceso a su interior y hace esto:

namespace hacker { struct unique {}; // Create a new unique type to avoid breaking ODR template <> std::ostream& operator<< <unique>( std::ostream&, const Test<unique>& ) { // if Test<T> is an extrovert, I can access and modify *any* Test<T>!!! // if Test<T> is an introvert, then I can only mess up with Test<unique> // which is just not so much fun... } }


Esto funcionó para mí sin advertencias del compilador.

#include <iostream> using namespace std; template <class T> T my_max(T a, T b) { if(a > b) return a; else return b; } template <class classT> class D { public: D(classT in) : d(in) {}; bool operator>(const D& rhs) const { return (d > rhs.d); } classT operator=(const D<classT>& rhs); friend ostream& operator<< (ostream & os, const D& rhs) { os << rhs.d; return os; } private: classT d; }; int main() { int i1 = 1; int i2 = 2; D<int> d1(i1); D<int> d2(i2); cout << my_max(d1,d2) << endl; return 0; }


No puede declarar a un amigo así, necesita especificar un tipo de plantilla diferente para él.

template <typename SclassT> friend ostream& operator<< (ostream & os, const D<SclassT>& rhs);

nota SclassT para que no classT . Al definir

template <typename SclassT> ostream& operator<< (ostream & os, const D<SclassT>& rhs) { // body.. }