surcharge operator friend << pour la classe template

J’ai lu quelques questions concernant mon problème sur stackoverflow maintenant, et rien ne semble résoudre mon problème. Ou je me suis peut-être trompé … Le surchargé << si je rentre 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&)' 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 >&, D const&)' collect2: ld returned 1 exit status

Le code:

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

C’est l’une de ces questions fréquemment posées qui ont des approches différentes, similaires mais pas vraiment identiques. Les trois approches diffèrent quant à savoir qui vous déclarez être un ami de votre fonction – et ensuite sur la façon dont vous l’implémentez.

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 proposent la plupart des autres réponses. Dans cette approche, vous ouvrez inutilement votre instanciation particulière D en déclarant des amis à tous les operator<< instanciations. C'est-à-dire que std::ostream& operator<<( std::ostream &, const D& ) a access à tous les std::ostream& operator<<( std::ostream &, const D& ) internes de D .

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

Les introvertis

Ne déclarez qu'une instanciation particulière de l'opérateur d'insertion en tant qu'ami. D 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& ) .

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

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

Dans cette première version, vous ne créez pas un operator<< un modèle operator<< , 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 revient à append manuellement: std::ostream& operator<<( std::ostream&, const Test& ) lorsque vous instanciez Test , et une autre surcharge similaire lorsque vous instanciez Test avec double ou avec tout autre type.

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

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

Profitant de l'extraverti

La différence subtile entre cette troisième option et la première est dans combien vous ouvrez à d'autres classes. Un exemple d'abus dans la version extravertie serait une personne qui veut avoir access à vos internes et fait ceci:

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

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

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

notez SclassT pour qu’il ne classT pas le classT . Lors de la définition

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

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

 #include  using namespace std; template  T my_max(T a, T b) { if(a > b) return a; else return b; } template  class D { public: D(classT in) : d(in) {}; bool operator>(const D& rhs) const { return (d > rhs.d); } classT operator=(const D& 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 d1(i1); D d2(i2); cout << my_max(d1,d2) << endl; return 0; } 

Voici:

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

Je pense que vous ne devriez pas vous faire d’amis en premier lieu.

Vous pouvez créer un appel à une méthode publique, quelque chose comme ceci (pour une classe autre que le 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 ensuite, 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 fonctionner aussi pour la classe de modèle, mais je n'ai pas encore testé.