Newsgroup: it.comp.lang.c++
Titolo: problemi con template

Ho dei dubbi riguardo ai template.
Considerate la seguente classe complex
#include <iostream>
template<class T> class complex
{
   private:
      T re_, im_;
   public:
      template<class U> friend class complex;
      complex(const T& r, const T& i)
      : re_(r), im_(i) {  }
      // se complex<U> non č friend, other.re_ e other.im_ sono
      // inaccessibili
      template<class U> complex(const complex<U>& other)
      : re_(other.re_), im_(other.im_) {  }
      const T& real() const { return re_; }
      const T& imag() const { return im_; }
      friend std::ostream&
      operator<<(std::ostream& os, const complex& z);
};
1) Perchč se non rendo complex<U> friend le sue variabili private
   non sono accessibili?
2) Se, con il BCC 5.5, faccio
       template<class U> friend class complex<U>;
   il compilatore mi dice:
   Error: Specialization within template classes not yet
          implemented
   A che cosa si riferisce?
   Invece il DJGPP mi dice:
   :variable `class complex<int> ci' has initializer but incomplete
   type
   :variable `class complex<float> cf' has initializer but incomplete
   type
   : confused by earlier errors, bailing out
Considerate adesso:
template<class T>
std::ostream& operator<<(std::ostream& os, const complex<T>& z)
{
   return os << '(' << z.re_ << ", " << z.im_ << ')';
}
int main()
{
   complex<int> ci(3, 4);
   complex<float> cf = ci;
   std::cout << cf << '\n';
}
Con il BCC 5.5 ottengo l'errore:
Error: Unresolved external 'operator <<(std::basic_ostream<char,
       std::char_traits<char> >&, const complex<float>&)'
       referenced from ...
Con il DJGPP č lo stesso, ma in piů mi da il warning:
warning:   friend declaration `class ostream & operator <<(ostream &,
           const complex<T> &)'
warning:   declares a non-template function
warning:   (if this is not what you intended, make sure
warning:   the function template has already been declared,
warning:   and add <> after the function name here)
warning:   -Wno-non-template-friend disables this warning.
Che vuole dire?
Ho provato a dichiarare (nella classe):
      template<class U>
      friend std::ostream&
      operator<<(std::ostream& os, const complex<U>& z);
al posto della precedente.
Con il BCC 5.5 mi da ancora l'unresolved external reference,
mentre con il DJGPP compila!
Allora č questa la sintassi per dichiarare funzioni template
friend? Quindi il BCC 5.5 non le supporta?
Con la precedente sintassi che cosa stavo dichiarando?
Grazie
Manlio Perillo

    : confused by earlier errors, bailing out
Boh!

Non sono sicuro, ma forse la funzione friend che e' template sullo
stesso argomento va definita come:
template<class T> class complex
{
...
      friend std::ostream&
      operator<< <> (std::ostream& os, const complex& z);
};
Non so se risolvi i problemi con entrambe i compilatori, pero'.
Ciao,
Nicola

 Con la precedente sintassi che cosa stavo dichiarando?
Credo che stessi rendendo friend un'altra funzione non template, che però
non era definita.
Max


In effetti è quanto lascia intendere il warning del DJGPP.
Però complex è un template, mah che casino...
Con i template i warning e gli error's reports sono poco
chiari.
Mi piacerebbe sapere quali warning/error danno altri compilatori per:
      friend std::ostream&
      operator<< (std::ostream& os, const complex& z);
e
     template<class U> friend class complex<U>;
Manlio Perillo

 
Non conoscevo questa sintassi.
Al DJGPP va bene.
Con il BCC 5.5 ottengo sempre l'unresolved external reference.
Mi sembra una limitazione abbastanza grave...
Manlio Perillo

 [...]
I dubbi sono aumentati quando mi sono accorto di questo:
#include <iostream>
class foo
{
   public:
      template<class Q> void do_foo()
      {
         std::cout << "foo::do_foo()\n";
      }
};
int main()
{
   foo f;
   f. template do_foo();
}
Il programma con il BCC 5.5 compila silenziosamente, ma alla sua
esecuzione non viene chiamata foo::do_foo()!!!
Non riesco proprio a capire come interpreta e che cosa fa con
f. template do_foo()!
Il DJGPP invece giustamente mi da un errore:
: In function `int main()':
: parse error before `('
Infine c'è un'altro problema.
Lo Stroustrup nella appendice C.13.6 del suo libro, dice
chiaramente che per accedere a foo::do_foo la sintassi è
(ad esempio):
    f. template do_foo<int>();
mentre
    f.do_foo<int>();
è un errore.
Ma sia il BCC 5.5 che il DJGPP compilano senza problemi
    f.do_foo<int>();
!!
E' una estesione?
E' un bug?
Manlio Perillo

 è un errore.
Nell'esempio dello Stroustrup la classe era un parametro di
template, quindi il compilatore non poteva distinguere una
chiamata a membro template da un confronto.
E' come se nel nostro caso ci fosse una funzione
  template <typename T>
  void func( T t )
  {
    t.do_foo<int>(); //errore
    t.template do_foo<int>();
  }
Senza "template" il compilatore non puo' sapere cosa sia T::do_foo
e interpreta  t.do_foo<int  come un confronto, segnalando l'errore
(int non e' una espressione valida per '<').
Nel caso orginale il compilatore sa che f e' di tipo foo e che
foo::do_foo e' un membro template, quindi non c'e' errore.
	Mauro


Ok, grazie per la spiegazione.
Tuttavia il BCC 5.5 compila ugualmente il tuo esempio di func!
(mentre il DJGPP da errore).
A quanto pare il BCC 5.5 ha dei bug alquanto gravi(?).
Manlio Perillo


Non č ancora finita...
Mi sono proprio ora accorto di una cosa interessante.
#include <stdexcept>
template<class T> class Handle
{
   private:
      T* pH;
      Handle(const Handle&);  // non definito
   public:
      explicit Handle()
      : pH(0) {  }
      T* operator->() const
      {
         if(!pH) throw std::runtime_error("puntatore nullo");
         return pH;
      }
   private:
      friend typename T;
      Handle& operator=(T* pT)
      {
         pH = pT;
         return *this;
      }
};
#include <iostream>
class foo;
Handle<foo> handle;
class foo
{
   public:
      foo()
      {
        handle = this;
      }
      void do_foo() { std::cout << "foo::do_foo\n"; }
};
int main()
{
   foo k;
   handle->do_foo();
}
Cosě come č con il BCC 5.5 funziona!
Tuttavia il DJGPP non me lo compila, rifiutando
l'espressione
    friend typename T;
Un'altra cosa strana (per me), e che se faccio
    friend class T
il programma non compila piů nemmeno con il BCC 5.5.
Gli errori che ottengo sono:
--BCC 5.5--
Error: 'Handle<foo>::operator =(foo *)' is not accessible in
       function foo::foo()
--DJGPP--
: using template type parameter `T' after `class'
Non č ancora finita :-)
Se faccio semplicemente
    friend T;
il programma compila con entrambi i compilatori....
Quindi č possibile fare una cosa del genere?
Quale č la sintassi corretta?
Grazie
Manlio Perillo


Scusate se sto ingrossando questo thread, ma non dipende da
me.
Nel paragrafo 13.5 del suo libro Stroustruop dice:
"All specializations of a template must be declared in the same
 namespace as the template itself".
E' una cosa ovvia, ma l'ho voluta testare:
#include <iostream>
namespace A
{
   template<class T> struct foo
   {
      foo()
      {
         std::cout << "foo<T>\n";
      }
   };
}
namespace B
{
   using A::foo;
   template<> struct foo<int>
   {
      foo()
      {
         std::cout << "foo<int>\n";
      }
   };
}
int main()
{
   A::foo<int> h;
   B::foo<int> h2;
}
Con il BCC 5.5 compila!!!
Inoltre l'output č:
foo<int>
foo<int>
Assurdo....
Con il DJGPP invece non compila:
: specializing `struct A::foo<int>' in different namespace
: from definition of `template <class T> struct A::foo<T>'
Che cosa ne pensate?
(io penso che il BCC 5.5 sia una mina vagante)
Manlio Perillo

 (io penso che il BCC 5.5 sia una mina vagante)
Beh, ormai l'abbiamo capito. :-( A quando la nuova patch?...
EMG

    complex<float> cf = ci;
Naturalmente qui il compilatore mi blocca, perchè senza la dichiarazione
friend, non posso accedere ai membri privati di complex<int>.
L'unico modo che ho trovato per farlo compilare è mettere: friend
complex<float>; nella classe complex.
Ciao Fulvio.


La cosa è prevista dallo Standard.
Manlio Perillo

      friend typename T;
Il mio e' solo un sospetto (non essendo un guru di questa
"robaccia" :D) ... ma credo che quando scrivi
   template<class T> ...
il compilatore sa che T e' una classe come tale e' superfluo
dire "class T" quando usi il nome T nel corpo del template.
Sarebbe un po' come dire "class class X".
A intuito troverei quindi corretta la sintassi
   friend T; // Il compilatore sa gia' che T e' una classe
Che in effetti viene accettata da entrambi i compilatori.
Il mio sospetto sul comportamento "strano" del BCC55 e' che
quando scrivi
   friend class T;
il sistema intenda a una classe di nome "T" dichiarata in
questo punto come forward (quindi con 'class') mentre gcc
interpreta il parametro template e non digerisce questa
"doppia class" in una dichiarazione friend.
Questa convinzione mi viene dal fatto che con BCC55 il
seguente programma compila senza problemi...
  template<class T> class X
  {
    private:
      T t_;
    public:
      X(const T& t) : t_(t) { }
      friend class T; // Questo *NON* e' il parametro template
  };
  class T // Questa e' la classe "friend"
  {
    public:
      template<class W>
      void PrintIt(const X<W>& x)
      {
        std::cout << x.t_ << std::endl;
      }
  };
  int main()
  {
    X<int>    ix(6502);
    X<double> dx(65.02);
    T printer;
    // Nessun problema anche se i membri
    // ix.t_ e dx.t_ sono private
    printer.PrintIt(ix);
    printer.PrintIt(dx);
 
    return 0;
  }
 
Lo stesso programma puo' essere compilato con gcc
solo se si cambia nome al parametro template.
Non so cosa dica lo standard... comunque si tratta
di una carenza del gcc/djgpp oppure di una estensione
non standard del BCC55.
Quando scrivi
   friend typename T;
non riesco ad immaginare cosa intenda il BCC55...
forse semplicemente "ignora" la parola typename e
prosegue interpretando "friend T". Oppure forse questo
e' quanto richiesto dallo standard per chiarire che si
sta parlando del parametro template (ne dubito... ma
non ho lo standard e quindi non lo so).
HTH
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

       friend typename T;
Non si puo' fare.
Le dichiarazioni friend devono essere del tipo
  friend class T;
e non
  friend T;
Pero' in una dichiarazone del tipo  "class T", T _non_ puo'
essere un parametro di template.
C'e' un thread sullo stesso argomento in comp.std.c++,
dal titolo "template and friends" (fine ottobre).
	Mauro

 ha, non si decide a rendere il suo compilatore Standard compliant?
leggendo un'intervista fatta al direttore del progetto Visual Studio
di MS, appare chiarissimo che rendere il compilatore standard
compliant sia l'ultimo dei loro problemi.