Newsgroup: it.comp.lang.c++
Titolo: Templates & friendship

Questo mi sembra interessante.
Compilate il seguente codice:
---
template <class T>
class A
{
//public:
	typedef int Ret;
//private:
	int a;
	friend typename A<T>::Ret f(A<T>);
	friend void g(A<T> a, typename A<T>::Ret& out);
	friend typename int h(A<T>);
public:
	A() : a() {}
};
template <class T>
typename A<T>::Ret f(A<T> a) { return a.a; }
template <class T>
void g(A<T> a, typename A<T>::Ret& out) { out = a.a; }
template <class T>
int h(A<T> a) { typename A<T>::Ret tmp = a.a; return tmp; }
int main()
{
	A<int> a;
	int ok;
	ok = f(a);
	g(a, ok);
	ok = h(a);		// questa è sempre ok!
}
---
La terzultima e la quartultima riga generano un errore. Eliminando i 
commenti, il codice si compila.
Non è strano? E' evidente cosa succede, ma mi meraviglio che succeda.
Kiuhnm

Kiuhnm wrote:
[...]
Per quanto riguarda la mia passata critica verso la sintassi "template 
<...> friend", guardate questo codice:
---
template <class T>
class A
{
	int a;
	friend int s<T>();
public:
	A() : a() {}
};
template <class T>
int s() { A<T> a; return a.a; }
int main()
{
	A<int> a;
	int ok = s<int>();
}
---
Come fate a rendere friend soltanto l'istanza giusta?
E' chiaro che esistono artifici per superare il problema, ma non vale la 
pena.
Ma ha senso un simile codice?
Includere una sola istanza di 's' potrebbe servire come controllo extra 
in presenza di costrutti complicati.
Per es., in qualche modo contorto, potrebbe venire generata una funzione 
del tipo:
int s<int>() { A<double> a; return a.a; }
e noi non vogliamo questo. Non vogliamo cioč che s<int> acceda a un A<T> 
con T != int.
Kiuhnm

 Kiuhnm
Per me è a posto.
Forse non ho capito bene che cosa intendi. Potresti spiegarmi?
Ugo

 Forse non ho capito bene che cosa intendi. Potresti spiegarmi?
Il C++ limita la friendship al solo corpo delle funzioni! E' assurdo!
Abbiamo una funzione che è dichiarata come friend in 'A', ma non può 
accedere a 'Ret' perché privato!
Kiuhnm

Kiuhnm ha scritto:

Io vedo la stessa situazione in un altro modo.
Per me è la funzione main() che non può accedere a Ret,
quindi all'interno della funzione main() il tipo:
   typename A<int>::Ret (*f_ptr) (A<int>)
non può essere risolto.
Ugo

 Come fate a rendere friend soltanto l'istanza giusta?
lo hai gia' fatto ;-)
Tommaso


Non sono d'accordo principalmente per due motivi:
1) main() non nomina mai A<int>::Ret;
2) Aggiungere 'friend int main()' in A non risolve il problema.
Kiuhnm

 }
ci sono degli errori nel tuo codice... scusa, perche' non fai cosi come
segue?
template <class T>
class A
{
//public:
        typedef int Ret;
//private:
        int a;
        friend typename A<T>::Ret f(A<T> a) { return a.a; }
        friend void g(A<T> a, typename A<T>::Ret& out) { out = a.a; }
        friend int h(A<T> a) { typename A<T>::Ret tmp = a.a; return
tmp;} 
public:
        A() : a() {} 
}; 
Tommaso

         friend int h(A<T> a) { typename A<T>::Ret tmp = a.a; return
[...]
Perché volevo mostrare alcune inconsistenze (o, se vuoi, scelte poco 
felici) del C++.
Kiuhnm

 qua dici che s<T> e' friend. Non qualsiasi istanza di s, solo per T.
Hai provato a compilarlo?
Kiuhnm

Kiuhnm ha scritto:

Questo compila:
//-- start kiuhnm01.cpp
template <class U>
int s();
template <class U>
int f();
template <class T>
class A
{
    int a;
    // s<U> ed f<U> sono friend di A<Type0>
    // solo se "U == Type0"
    friend int s<T>();
    friend int f<T>();
    // g<U>() è friend di A<T> per qualsiasi
    // combinazione di U e T
    template <typename U>
    friend int g();
public:
    A() : a() {}
};
template <class U>
int g(){ A<double> a; return a.a; }
template <class U>
int s() { A<U> a; return a.a; }
template <class U>
int f() { A<double> a; return a.a; }
int main()
{
    int ok = s<int>();
    ok = s<double>();
    // Questa non compila. Infatti f<int>() non è friend di A<double>.
    // int ko = f<int>();
    // Solo f<double> è friend di A<double>.
    ok = f<double>();
    // Queste sono ok perchè g<U>() è friend di A<T>
    // per qualunque combinazione dei parametri T e U.
    // g<U> accede a A<double>::a
    ok = g<int>();
    ok = g<char>();
}
//-- end kiuhnm01.cpp
Ugo
P.S. I tuoi post sono sempre molto interessanti :-)

 wrote 




Come vuoi... questo compila:
template <class T>
int s();
template <class T>
class A
{
	int a;
	friend int s<T>();
public:
	A() : a() {}
};
template <class T>
int s() { A<T> a; return a.a; }
int main()
{
	A<int> a;
	int ok = s<int>();
}
e se cambio:
template <class T>
int s() { A<T> a; return a.a; }
in:
template <class T>
int s() { A<double> a; return a.a; }
ottengo:
$ gcc ./prova.cpp
./prova.cpp: In function `int s() [with T = int]':
./prova.cpp:21:   instantiated from here
./prova.cpp:7: error: `int A<double>::a' is private
./prova.cpp:16: error: within this context
Tommaso
-- 
Posted via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 Eliminando i  commenti, il codice si compila.
gcc dice:
$ gcc ./prova.cpp
./prova.cpp:9: warning: friend declaration `int f(A<T>)' declares a
non-template
 function
./prova.cpp:9: warning: (if this is not what you intended, make sure the
functio
n template has already been declared and add <> after the function name
here) -W
no-non-template-friend disables this warning
./prova.cpp:10: warning: friend declaration `void g(A<T>, int&)'
declares a non-
template function
./prova.cpp:11: error: expected nested-name-specifier before "int"
./prova.cpp:11: error: ISO C++ forbids declaration of `h' with no type
./prova.cpp:11: warning: friend declaration `int h(A<T>)' declares a
non-templat
e function


 P.S. I tuoi post sono sempre molto interessanti :-)
Altro che! Ho fatto una figuraccia :)
Non avevo visto che il problema era la mancanza della forward declaration :)
Kiuhnm


Certo che il gcc è decisamente un passo avanti nel rilevamento degli 
errori (specialmente per quanto riguarda i template).
Per es. il VC mi compila il seguente codice senza problemi, pur avendo 
disabilitato le estensioni ed usando il livello massimo di warning:
---
template <class T, class U>
int s() { A<T> a; A<U> b; return a.a + b.a; }
template <class T>
class A
{
     int a;
     template <class T>
     friend int s<T,T>();
public:
     A() : a() {}
};
int main()
{
     return s<int,double>();
}
---
Kiuhnm

 gcc dice:
[...]
Hai pensato che potrei non averlo compilato col gcc?
Kiuhnm

 Hai pensato che potrei non averlo compilato col gcc?
Si certo, ho immaginato. Presumere che compilare ed osservare il
comportamento == quello che dice lo standard e' una teoria che ancora
funziona poco nel mondo dei templates.
Io ti ho risposto senza provare di compilare il codice inizialmente. Poi
quando mi hai chiesto di compilarlo ho notato che vc++2005 non si
comportava bene ed ho provato comeau e gcc.
In altri casi vc++2005 si comporta meglio di gcc, dipende...
Un commento negativo che si puo' fare a mio avviso sul C++ e' che e'
talmente complesso che a 8 anni dallo standard (e 3 da una revisione
minore) ancora non solo i programmatori, ma anche i compilatori stessi
(peraltro scritti da programmatori) non riescano ad implementare
correttamente lo standard.


 correttamente lo standard.
Questo è certo! E sta per diventare ancora più complesso, ma a quel 
punto vi avrò già salutato :)
Kiuhnm

 declaration :)
Ho capito perché: spesso il VC compila anche in assenza di forward 
declaration necessarie. Questo mi ha abituato un po' male.
Kiuhnm

 punto vi avrò già salutato :)
Vedrai che Python ti piacerà :-)


Non scrivo spesso del codice con classi template e
funzioni friend template. Ho dovuto riguardare il cap.
8.4 di Vandevoorde - Josuttis prima di scrivere il codice ;-)
Ugo
P.S.
Una volta fatta la forward declaration, bisogna fare un'altra modifica:
[...]
template <class T>
class A {
[...]
    //template <class T> // <- confusione di VC
    friend int s<T,T>();
[...]
In questo modo l'esempio genera l'errore di compilazione
che si desiderava ( con VC e g++).

 che si desiderava ( con VC e g++).
Lo so che eliminando quella riga funziona, ma... è per questo che l'ho 
scritta :)
Spesso posto quesiti riguardanti cose che ritengo interessanti pur non 
essendo direttamente rilevanti in ambito lavorativo. Ogni tanto mi piace 
anche proporre qualche "sfida".
Kiuhnm

 Vedrai che Python ti piacerà :-)
No no no... sto andando in una direzione ben diversa.
Kiuhnm

 No no no... sto andando in una direzione ben diversa.
Ed essendo meno criptico?
Visto che ti stai scrivendo il tuo os non ti sara' venuto in mente di
scriverti anche un tuo linguaggio? ;-)


 scriverti anche un tuo linguaggio? ;-)
Precisamente, ma è tutto top secret ;)
Kiuhnm

 Precisamente, ma è tutto top secret ;)
Scusa la mia curiosita', ma lo fai nel tempo libero (beato te se ne
hai...) oppure dove lavori hanno tempo e soldi da spendere per
implementare un nuovo sistema oprativo + compilatore/linguaggio etc...?
Scusa se mi faccio i fatti tuoi, ma e' che nella mia ditta qualsiasi
cosa che il mercato chiede o che noi pensiamo il mercato apprezzi va
implementata in fretta per battera la concorrenza sul time to market
etc... quindi cerchiamo di scrivere davvero poco da capo figurati un
sistema opearativo... Al massimo compriamo un sistema opeartivo con i
suoi sorgenti e poi li modifichiamo a piacere per le modifiche che sono
necessarie.
Nello specifico lavoro in una ditta che produce software sia embedded
che basato su windows (a seconda del hardware su cui gira, ma il core
del codice e' tutto shared), scritto in C++ (la GUI/RUI e' in Java), per
fare mpeg ts analysis. Ovvero, ad esempio, per mediaset, rai, bbc,
echostar, etc.. per contorllare che quello che stanno mandando on air
sia corretto e segua i vari standard, mpeg2, dvb, atsc, isdb, etc...
Questa e' la base, poi da qui facciamo prodotti simili, complementari,
che vanno oltre e fanno anche content analysis e cosi' via.
Tu che lavoro fai?


 Tu che lavoro fai?
Puoi considerarmi un libero imprenditore/ricercatore.
Sono in fase di lancio.
Kiuhnm