Pregunta

He leído par de las preguntas con respecto a mi problema en StackOverflow.com ahora, y nada de esto parece resolver mi problema. O tal vez lo he hecho mal ... El << sobrecargado funciona si lo hago en una función en línea. Pero ¿cómo puedo hacer 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;
}
¿Fue útil?

Solución

Esta es una de las preguntas más frecuentes que tienen distintos enfoques que son similares pero no es realmente lo mismo. Los tres enfoques difieren en que está declarando ser un amigo de su función -y entonces sobre cómo ponerlo en práctica.

El extrovertido

Para contar todas las instanciaciones de la plantilla como amigos. Esto es lo que han aceptado como respuesta, y también lo que la mayoría de las otras respuestas proponen. En este enfoque va a abrir innecesariamente su D<T> instanciación particular, declarando amigos todas las instanciaciones operator<<. Es decir, std::ostream& operator<<( std::ostream &, const D<int>& ) tiene acceso a todas las partes 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

Sólo declarar una instanciación particular del operador de inserción como un amigo. D<int> le guste 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 puede hacerse de dos formas, la forma más sencilla de ser tan @Emery Berger propuso, que se inlining el operador -que también es una buena idea por otras razones:

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, usted es no crear un operator<< con plantilla, sino más bien una función no con plantilla para cada instancia de la plantilla Test. Una vez más, la diferencia es sutil pero esto es básicamente equivalente a añadir manualmente:. std::ostream& operator<<( std::ostream&, const Test<int>& ) cuando Test<int> instantiate, y otra sobrecarga similares cuando Test instantiate con double, o con cualquier otro tipo

La tercera versión es más engorroso. Sin inlining el código, y con el uso de una plantilla, se puede declarar una única instancia de la plantilla de un amigo de su clase, sin abrirse a todos otras 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 está en la cantidad que se está abriendo a otras clases. Un ejemplo de abuso en el extrovertida versión habría alguien que quiera acceder a sus componentes internos y hace lo siguiente:

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

Otros consejos

No se puede declarar un amigo así, es necesario especificar un tipo de plantilla diferente para él.

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

nota SclassT por lo que no classT sombra. Al definir

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

Esto funcionó para mí sin ningún tipo de 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;
}

Aquí van:

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

Creo que no se debe hacer al amigo en el primer lugar.

Se puede crear una impresión llamada a un método público, algo como esto (para una clase no plantilla):

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

y, a continuación, 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 no he probado todavía.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top