Question

J'ai lu quelques questions au sujet de mon problème sur StackOverflow.com maintenant, et rien ne semble résoudre mon problème. Ou peut-être que je l'ai fait mal ... La surcharge << fonctionne si je 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;
}
Était-ce utile?

La solution

Ceci est l'une de ces questions fréquemment posées qui ont des approches différentes qui sont similaires mais pas vraiment la même chose. Les trois approches diffèrent qui vous déclarez être un ami de votre fonction --et puis sur la façon dont vous le mettre en œuvre.

extravertie

Déclarez toutes les instanciations du modèle comme des amis. Voici 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 D<T> particulière instanciation en déclarant amis tous instanciations operator<<. C'est, 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éclarer Seule une instanciation particulière de l'opérateur d'insertion comme un ami. D<int> peut que l'opérateur d'insertion lorsqu'il est appliqué à lui-même, mais il ne veut rien à voir avec std::ostream& operator<<( std::ostream&, const D<double>& ).

Cela peut se faire de deux façons, la manière simple comme étant @Emery Berger proposé, qui est l'opérateur inline --which est aussi 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 êtes pas la création d'un operator<< basé sur un modèle, mais plutôt une fonction non pour chaque instanciation templated du modèle de Test. Encore une fois, la différence est subtile, mais cela est essentiellement équivalent à ajouter manuellement. std::ostream& operator<<( std::ostream&, const Test<int>& ) lorsque vous instancier Test<int>, et une autre surcharge similaire lorsque vous instancier Test avec double, ou avec tout autre type

La troisième version est plus lourde. Sans inline le code, et l'utilisation d'un modèle, vous pouvez déclarer une seule instanciation du modèle un ami de votre classe, sans vous ouvrir à tous 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 la extravertie

La différence subtile entre cette troisième option et le premier est combien vous ouvrez à d'autres classes. Un exemple d'abus dans le extravertie version 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...
   }
}

Autres conseils

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

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

SclassT afin qu'elle ne l'ombre pas classT. Lors de la définition

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

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

vous allez ici:

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

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

Vous pouvez créer une impression d'appel de méthode publique, quelque chose comme ça (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;
}

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

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

Je pense que cela devrait fonctionner aussi pour la classe de modèle, mais je ne l'ai pas encore testé.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top