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