2010-05-12 4 views
3

L'exemple suivant compile bien mais je ne peux pas comprendre comment séparer la déclaration et la définition de l'opérateur < <() est ce cas particulier.Comment diviser la définition de la fonction ami modèle dans la classe modèle?

Chaque fois que j'essaie de diviser l'ami de définition cause un problème et que gcc se plaint de l'opérateur < <() la définition doit prendre exactement un argument.

#include <iostream> 
template <typename T> 
class Test { 
    public: 
     Test(const T& value) : value_(value) {} 

     template <typename STREAM> 
     friend STREAM& operator<<(STREAM& os, const Test<T>& rhs) { 
      os << rhs.value_; 
      return os; 
     } 
    private: 
     T value_; 
}; 

int main() { 
    std::cout << Test<int>(5) << std::endl; 
} 

opérateur < <() est censé avoir un premier paramètre libre pour travailler avec différents types de flux de sortie (std :: Cout, std :: wcout ou boost :: asio :: :: ip tcp: : iostream). Le second paramètre devrait être lié à une version spécialisée de la classe environnante.

Test<int> x; 
some_other_class y; 

std::cout << x; // works 
boost::asio::ip::tcp::iostream << x; // works 

std::cout << y; // doesn't work 
boost::asio::ip::tcp::iostream << y; // works 

En outre en utilisant une fonction non-membre ne équivaut pas à diviser la définition et la déclaration parce que non-membres fonctions ne peuvent pas accéder attributs privés de la classe.

+0

dup possible: http://stackoverflow.com/questions/476272/how-to-properly-overload-the-operator-for-an-ostream – Stephen

+0

Pourquoi voulez-vous paramétrer le type OSTREAM? idiome commun serait l'opérateur << comme définir: 'ami std :: ostream & operator << (std :: ostream & os, Test & rhs) {...}' –

+0

@dribeas: parce que je veux être en mesure d'utiliser d'autres flux de sortie aussi bien. Comme boost :: asio :: ip :: tcp :: iostream et boost :: asio :: local :: stream_protocol :: iostream. – joke

Répondre

4

Le plus simple est sans doute de faire tous ces opérateurs de modèle amis:

#include <iostream> 
template <typename T> 
class Test 
{ 
    public: 
     Test(const T& value) : value_(value) {} 

     template <typename STREAM, typename U> 
     friend STREAM& operator<<(STREAM& os, const Test<U>& rhs); 

    private: 
     T value_; 
}; 

template <typename STREAM, typename T> 
STREAM& operator<<(STREAM& os, const Test<T>& rhs) 
{ 
    os << rhs.value_; 
    return os; 
} 
+0

spécifiant un second paramètre de modèle pour l'opérateur <<() fonctionne. – joke

+0

je ne pouvais pas trouver un moyen de réutiliser 'T' soit, même la fonction de membre typique du modèle de déclaration ayant 2' template' précédant la définition de la fonction. Cela n'a pas d'importance en termes d'accessibilité, mais cela semble néanmoins étrange. –

1

Ne devrait-il pas être défini en dehors de la classe?

template <typename T> 
class Test 
{ 
    ... 
    template <typename STREAM> 
    friend STREAM& operator<<(STREAM& os, const Test<T>& rhs); 
}; 

template <typename STREAM, typename T> 
STREAM& operator<<(STREAM& os, const Test<T>& rhs) 
{ 
    os << rhs.value_; 
    return os; 
} 
+0

Avez-vous essayé de compiler ceci? –

+0

normalement vous écririez: modèle template STREAM & Test :: operator << (STREAM & os, Test & const rhs) { os << rhs.value_; return os; }; Mais cela ne fonctionne pas dans ce cas. – joke

+0

Même si cela fonctionnerait, l'exemple manque toujours: Test :: operator << (...) – joke

1

Le plus proche que je peux atteindre est

#include <iostream> 

template <typename T> 
class Test; 

template <typename STREAM, typename T> 
STREAM& operator<<(STREAM& os, const Test<T>& rhs); 

template <typename T> 
class Test { 
public: 
    Test(const T& value) : value_(value) {} 

    template <typename STREAM, typename U> 
    friend STREAM& operator<< (STREAM& os, const Test<U>& rhs); 

private: 
    T value_; 
}; 

template <typename STREAM, typename T> 
STREAM& operator<<(STREAM& os, const Test<T>& rhs) { 
    os << rhs.value_; 
    return os; 
} 

int main() { 
    std::cout << Test<int>(5) << std::endl; 
} 

qui déclare tout opérateur < < comme ami au lieu de seulement celui qui est paramétré par T. Le problème est qu'il n'est pas possible de spécialiser partiellement les fonctions. On aurait aimé utiliser

template <typename STREAM> 
friend STREAM& operator<< <STREAM, T> (STREAM& os, const Test<T>& rhs); 

mais cette syntaxe n'est pas valide. (Bien, et la spécialisation partielle ne peut pas être déclarée ami)

+0

Vous avez raison, ce n'est pas la même chose que l'original. Mais puisque la version originale peut être définie dans la définition de classe, il devrait y avoir un moyen de séparer la déclaration et la définition. – joke

+0

L'original définit une fonction avec un paramètre de modèle, le premier. Si vous voulez quelque chose d'équivalent, vous devrez définir une fonction par type T. C'est possible, mais probablement douloureux. – AProgrammer

+0

Oui, il déclare/définit la fonction membre du modèle avec un paramètre de modèle dès que la classe de modèle surround est instanciée. – joke

0

Le problème est que dans le code que vous présentez l'ami est une fonction modélisée paramétrée uniquement sur le premier type d'argument. C'est, pour chaque type d'instanciation T du modèle de classe (appeler mytype), vous déclarez une fonction de modèle gratuit:

template <typename STREAM> 
STREAM& operator<<(STREAM& os, Test<mytype> const & x); 

Le point important, il est que Test<mytype> est le instanciation particulier de Test avec l'argument de type mytype . Si vous voulez vraiment déclarer une fonction ami qui est modélisée dans le type de flux et le type d'instanciation du modèle Test, vous devez déclarer un ami avec deux arguments. D'autre part, je vous recommande de ne pas paramétrer operator<< sur le type de flux, et en même temps, de le définir dans les accolades de classe car il présente de légers avantages (les règles de recherche de nom sont légèrement différentes).

+0

@dribeas: http://stackoverflow.com/questions/2819994/2820743#2820743 – joke

0

Pour chaque type instancié T de classe Test, un opérateur de fonction de gabarit < <() est exposé qui peut fonctionner sur différents types de flux. La fonction opérateur < <() a un premier paramètre libre mais un second paramètre fixe.

exemple:

Test<int> x; 
some_other_class y; 

std::cout << x; // works 
boost::asio::ip::tcp::iostream << x; // works 

std::cout << y; // doesn't work 
boost::asio::ip::tcp::iostream << y; // works 

C'est la façon dont la classe d'essai était censé fonctionner.

+0

Ne pas 'boost :: asio :: :: ip tcp :: iostream' et' boost :: asio :: ip :: tcp :: iostream' hérite de 'std :: basic_iostream' qui hérite lui-même de' std :: basic_ostream' ?? –

+0

Je ne pense pas qu'ils héritent. Ils devraient être des spécialisations de modèle. boost :: asio :: :: ip tcp :: iostream devrait être une spécialisation du modèle basic_socket_iostream. – joke

+0

Il suffit de suivre le code ... (boost/asio/basic_socket_iostream.hpp: 'modèle <...> classe basic_socket_iostream: public boost :: base_from_member <...>, public std :: basic_iostream ').Cela a du sens, tout le problème est que vous n'avez pas besoin de réécrire n'importe quel 'operator <<' pour utiliser les iostreams asio. Si vous pouvez écrire dans 'cout', vous pouvez écrire sur un flux TCP. –