l’overload dell’operatore amico<< per la classe modello

Ho letto un paio di domande in merito al mio problema StackOverflow.com ora, e nessuno sembra risolvere il mio problema. O forse hanno fatto male…
Il sovraccarico << funziona se lo faccio in una funzione inline. Ma come faccio a farlo funzionare nel mio caso?

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

Codice:

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’è stato un recente domanda su questo che può essere istruttivo: stackoverflow.com/questions/4571611/virtual-operator
  • non prende il problema che ho quando il sovraccarico per un modello di classe
  • Penso che è meglio se non si modifica la domanda con una risposta data. Rende più difficile per determinare che cosa il problema originale era. Si potrebbe desiderare di aggiungere un MODIFICA alla fine con il cambiamento che si è risolto il problema, ma io la trovo confusa quando le domande modificare gli straordinari e devo tirare la storia per vedere che cosa era in realtà stato chiesto in primo luogo.
InformationsquelleAutor starcorn | 2011-01-11



5 Replies
  1. 138

    Questa è una delle domande più frequenti che hanno approcci diversi che sono simili ma non proprio uguali. I tre approcci diversi in chi dichiara di essere un amico della vostra funzione, e quindi su come implementare.

    L’estroverso

    Dichiarare tutte le istanze del modello con gli amici. Questo è ciò che avete accettato come risposta, e anche ciò che la maggior parte delle altre risposte proposte. In questo approccio si sono inutilmente apertura di un particolare istanza D<T> dichiarando amici tutte operator<< istanze. Che è, std::ostream& operator<<( std::ostream &, const D<int>& ) ha accesso a tutte le parti interne del 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
    }

    Il introversi

    Solo dichiarare una particolare istanza di inserimento come operatore di un amico. D<int> , come può l’operatore di inserimento quando viene applicato a se stesso, ma non voglio niente a che fare con std::ostream& operator<<( std::ostream&, const D<double>& ).

    Questo può essere fatto in due modi, il modo più semplice per essere come @Smeriglio Berger proposta, che è l’inlining l’operatore-che è anche una buona idea per altri motivi:

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

    In questa prima versione, si sono non la creazione di un basato su modelli operator<<, ma piuttosto un non-funzione basato su modelli per ogni istanza di Test modello. Di nuovo, la differenza è sottile, ma questo è fondamentalmente l’equivalente di aggiungere manualmente: std::ostream& operator<<( std::ostream&, const Test<int>& ) quando si crea un’istanza di Test<int>, e un altro simile sovraccarico quando si crea un’istanza di Test con double, o con qualsiasi altro tipo.

    La terza versione è più ingombrante. Senza l’inlining il codice, e con l’utilizzo di un modello, è possibile dichiarare una singola istanza del modello di un amico di classe, senza aprire voi stessi di tutti altre istanze:

    //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> ...
    }

    Sfruttando l’estroverso

    La sottile differenza tra questa terza opzione, e il primo è in quanto si sta aprendo alle altre classi. Un esempio di abuso in estroverso versione sarebbe qualcuno che vuole ottenere l’accesso alla propria interni e le fa:

    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...
       }
    }
    • in modo molto più chiaro che la risposta selezionata
    • Implementare la funzione al di fuori della classe dà un riferimento non definito. All’interno della classe ho un problema, appena ho esplicitamente instanciate host classe con più di un tipo.
    • Non posso eseguire il debug di codice che non sto vedendo, ma funziona. È l’amico dipendente su qualsiasi modello di argomenti? Se non vuoi avere problemi con più definizioni diverse istanze genera esattamente il stesso funzione (o un ODR violazione se la firma della funzione è la stessa, ma il corpo è diverso).
    • Questo continua ad essere uno dei miei più di cui go-to per le persone in cerca di amico delle funzioni del modello (non solo degli operatori), dovuto in non piccola parte le notevoli differenze nei modi in cui può essere fatto, e i vantaggi e detrazioni per ogni. Solo una stellar risposta, David. Mi piacerebbe uptick una dozzina di volte, se potessi.
    • grande! Grazie!
    • Nella terza versione è “operatore<< <T>” questa linea significa creare un’istanza del modello di funzione di operatore << come un amico di modello di classe di Prova con la stessa T ?
    • Sì, e che la specializzazione è friended a, e solo, che friended-modello di specializzazione. I. e. Se si dispone di alcuni Test<T> il solo operator << friended che è la operator << <T> istanza. Arbitrario Test<U>, dove U non è sinonimo di T, non amico operator << <T>; essi amici operator << <U> invece. Potrebbe non sembrare un grande affare, ma può tranquillamente diventare uno senza rendersene conto.
    • ben studiato, ma io non sono del tutto chiaro come vorrei risolvere il seguente in “Introverso” di stile : wandbox.org/permlink/jMKpn6CKFL2cyceO … sembra Che se mi piace conservare il pieno di incapsulamento di tipo annidato nomi che non è possibile utilizzare l’operatore semplice sovraccarico su di loro.

  2. 15

    Non è possibile dichiarare un /a amico /a, è necessario specificare un diverso tipo di modello per esso.

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

    nota SclassT in modo che non ombra classT. Quando si definisce

    template <typename SclassT>
    ostream& operator<< (ostream & os, const D<SclassT>& rhs)
    {
      //body..
    }
    • grazie funziona modificato la mia domanda con questo codice, mi spunta questo come risposta non appena il ticker va giù.
    • Si noti che questa non è la dichiarazione di una particolare istanza di operator<< come un amico, ma piuttosto tutti istanze, comprese eventuali specializzazione del modello. Vedi la risposta here
    • si consiglia di modificare la risposta selezionata per fornire la migliore risposta e che dovrebbe essere David Rodriguez risposta.
    • in base all’utente parere ho cambiato la risposta selezionata per la mia domanda. Comunque grazie per il tuo contributo per aiutare me in quel momento 🙂
    • nessun problema, ho votato per la risposta di David e via allora, è una trattazione più completa del problema..
  3. 3

    Questo ha funzionato per me, senza alcun avvisi del compilatore.

    #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;
    }
    • Sì, l’ho già fatto, ma che cosa succede se non voglio che il operator<< come una funzione inline?
    • Wheather un metodo/funzione taged inline (implicitamente o esplicitamente) ha poco a che fare previsioni la funzione è in realtà inline nel codice. Quindi, questo è privo di significato che preoccuparsi.
    • +1. @starcorn: Questa soluzione è migliore rispetto accettato uno. Il differce è sottile, ma ha accettato di rispondere, è la dichiarazione di tutte le istanze (e eventuali specializzazioni) del operator<< come amici, mentre in questa soluzione si sono solo di concedere l’accesso per la creazione di istanze di operator<< che ha lo stesso tipo di. Inoltre, come effetto collaterale di definire operator<< all’interno della classe si sono limitare la visibilità di che operator<< solo a quei casi in cui uno dei due argomenti è un D, il compilatore non sarà nemmeno in considerazione il operator<< sovraccarico, a meno che un argomento è D<T>.
    • Ho aggiunto una risposta che tenta di cancellare le differenze in tre diversi approcci here
  4. 0

    Qui si va:

    #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;
    }
    • Non penso che questo dovrebbe compilare: template<class classT> ostream& operator<<(ostream& os, class D<typename classT> const& rhs). Elaborato tipo non è consentito in dichiarazione di parametro e typename richiede qualificato-id.
    • hmm. non compilare per me massimi livelli con MS estensioni disattivate.
    • Non compilare con g++, e confido che il compilatore in questo. Il secondo argomento in operator<< è class D<typename classT>, e penso che non è corretto. Vorrei utilizzare D<classT> invece. La parola chiave class è facoltativo c’ (nel 99.9% dei casi), ma l’uso di typename non è uno dei due conoscere gli usi: sarebbe valido come un sostituto per il class, ed è per indicare che un dipendente nome su un modello è in realtà un tipo.
  5. 0

    Penso che non si deve fare amico, in primo luogo.

    È possibile creare un pubblico di chiamata di metodo di stampa, qualcosa di simile a questo (non modello di classe):

    std::ostream& MyClass::print(std::ostream& os) const
    {
      os << "Private One" << privateOne_ << endl;
      os << "Private Two" << privateTwo_ << endl;
      os.flush();
      return os;
    }

    e quindi, al di fuori della classe (ma nello stesso spazio dei nomi)

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

    Penso che dovrebbe funzionare anche per la classe del modello, ma non ho ancora testato.

Lascia un commento