web-dev-qa-db-fra.com

surcharge ami opérateur << pour la classe de modèle

J'ai lu quelques questions concernant mon problème sur StackOverflow.com maintenant, et rien ne semble résoudre mon problème. Ou je l'ai peut-être mal fait ... Le << Surchargé fonctionne si j'en fais une fonction en ligne. Mais comment puis-je le faire fonctionner dans mon cas?

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

Le code:

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;
}
52
starcorn

C'est une de ces questions fréquemment posées qui ont des approches différentes qui sont similaires mais pas vraiment les mêmes. Les trois approches diffèrent quant à la personne que vous déclarez être un ami de votre fonction - et ensuite à la façon dont vous la mettez en œuvre.

L'extraverti

Déclarez toutes les instanciations du modèle en tant qu'amis. C'est ce que vous avez accepté comme réponse, et aussi ce que la plupart des autres réponses proposent. Dans cette approche, vous ouvrez inutilement votre instanciation particulière D<T> En déclarant à vos amis toutes les instanciations operator<<. C'est-à-dire que std::ostream& operator<<( std::ostream &, const D<int>& ) a accès à tous les éléments internes 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
}

Les introvertis

Déclarez uniquement une instanciation particulière de l'opérateur d'insertion en tant qu'ami. D<int> Peut aimer l'opérateur d'insertion lorsqu'il est appliqué à lui-même, mais il ne veut rien avoir à faire avec std::ostream& operator<<( std::ostream&, const D<double>& ).

Cela peut se faire de deux manières, la manière la plus simple étant celle proposée par @Emery Berger, qui associe l'opérateur - ce qui est également une bonne idée pour d'autres raisons:

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

Dans cette première version, vous ne créez pas un modèle operator<<, Mais plutôt une fonction non modèle pour chaque instanciation du Test modèle. Encore une fois, la différence est subtile, mais cela revient à ajouter manuellement: std::ostream& operator<<( std::ostream&, const Test<int>& ) lorsque vous instanciez Test<int>, Et une autre surcharge similaire lorsque vous instanciez Test avec double, ou avec tout autre type.

La troisième version est plus encombrante. Sans insérer le code, et avec l'utilisation d'un modèle, vous pouvez déclarer une seule instanciation du modèle comme ami de votre classe, sans vous ouvrir à tout autres instanciations:

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

Profitant de l'extraverti

La différence subtile entre cette troisième option et la première réside dans le degré d'ouverture à d'autres classes. Un exemple d'abus dans la version extravertie serait quelqu'un qui veut avoir accès à vos internes et fait ceci:

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

Vous ne pouvez pas déclarer un ami comme ça, vous devez lui spécifier un type de modèle différent.

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

notez SclassT pour ne pas masquer classT. Lors de la définition

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

Cela a fonctionné pour moi sans aucun avertissement du compilateur.

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

Je pense que vous ne devriez pas vous faire d'ami en premier lieu.

Vous pouvez créer un appel de méthode publique print, quelque chose comme ceci (pour une classe non modèle):

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

puis en dehors de la classe (mais dans le même espace de noms)

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

Je pense que cela devrait également fonctionner pour la classe de modèles, mais je n'ai pas encore testé.

0
Alessandro Teruzzi

Voici:

#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;
}
0
John Dibling