c++ operateur surcharge de l'opérateur friend<< pour la classe de template




surcharge operateur<< c++ (4)

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

J'ai lu quelques questions concernant mon problème sur stackoverflow maintenant, et rien de tout cela ne semble résoudre mon problème. Ou je l'ai peut-être fait mal ... Le surchargé << si je le fais dans 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;
}

Answer #1

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

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

notez SclassT pour qu'il ne fasse pas d'ombre à classT . Lors de la définition

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

Answer #2

C'est l'une de ces questions fréquemment posées qui ont différentes approches qui sont similaires mais pas vraiment les mêmes. Les trois approches diffèrent selon qui vous déclarez être un ami de votre fonction - et ensuite sur la façon dont vous l'appliquez.

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 de l' operator<< . C'est-à-dire que std::ostream& operator<<( std::ostream &, const D<int>& ) a accès à tous les 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éclare seulement 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 façons, la manière la plus simple étant celle proposée par @Emery Berger, qui consiste à intégrer 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 operator<< un modèle, mais plutôt une fonction non basée sur un modèle pour chaque instanciation du modèle de Test . Encore une fois, la différence est subtile mais cela équivaut à 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 lourde. Sans inclure le code, et avec l'utilisation d'un template, vous pouvez déclarer une seule instanciation du template comme un ami de votre classe, sans vous ouvrir à toutes les 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 est de savoir combien vous ouvrez aux 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...
   }
}

Answer #3

Cela a fonctionné pour moi sans aucun avertissement de 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;
}




ostream