Newsgroup: it.comp.lang.c++
Titolo: riferimento di una variabile anonima

Salve gente,
  aiutatemi a capire!!
Ecco qual'e' la situazione:
ho creato una classe chiamata tempo.
In essa ho dichiarato una funzione "amica"
del tipo 
     friend ostream& operator<<(ostream&, tempo& )
Questa funzione mi serve per poter stampare il contenuto di un oggetto
di classe tempo .
Non ottengo nessun problema se scrivo tale codice.
-------------------------------------
#include <tempo.h>
void main()
{
   tempo t(12,22,10);   // richiamo il costruttore a 3 argomenti che
		  // ho definito per la classe tempo
   cout<<t;
}
------------------------------------
SE INVECE scrivo il sottostante codice succedono cose strane!
-------------------------------------
#include <tempo.h>
void main()
{
   cout<<tempo(12,22,10)
}
------------------------------------
Cosa intendo per "cose strane"???
Se compilo questo sorgente con il TurboC++3  non ottengo problemi
Tranuillamente compilo anche in Visual C++ 6 ( win32 consol appl.)
MENTRE se compilo con il Borland C++ Builder 5 mi viene proposto
l'errore  
[E2094 'operator<<' not implemented in type 'ostream' for arguments of
type 'tempo'].
Da quello che ho capito al builder non piace che passi per riferimento
un variabile anonima (creata tramite il construttore della classe
tempo).
Naturalmente se passo l'argomento TEMPO alla funzione amica OPERATOR<<
per valore ovvero
    friend ostream& operator<<(ostream&, tempo)
tutto fila liscio.
Ora mi/vi  domando:
e' un bug del Builder o un bug della mio cervello?
Grazie
~Guybrush~

 tempo).
In questo caso BCB 5 e' l'unico compilatore che si comporta in modo
corretto. Il C++ standard non consente di associare un valore temporaneo
ad un reference non const. Visto che presumibilmente il tuo operatore
non deve modificare l'argomento di tipo tempo ti consiglio di
dichiararlo come
ostream& operator<<(ostream &, const tempo &);
Ciao,
Nicola

ostream& operator<<(ostream &, const tempo &);
Perche' gli altri compilatori mi permettono di fare tale operazione?
Cmq devo dire che il Builder e' un buon RAD ma per compilare ci impega
una vita!!!
Il Visual e' stata invece una piacevole scoperta.
~Guybrush~

ostream& operator<<(ostream &, const tempo &);
Fermo restando che in quel caso la dichiarazione corretta era
"const tempo&", siamo sicuri che sia richiesto generare il
warning ? Quell'oggetto non e' secondo me una "temporanea" ma
semplicemente una istanza senza nome (pero' creata esplicitamente
dal programmatore e non dal compilatore in una, ad esempio,
conversione implicita).
Perche' dovrebbe essere vietato chiamare un metodo non-const
in questi casi ?
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

 in questi casi ?
Questo e' un discorso diverso: e' sempre possibile chiamare metodi non
const su oggetti temporanei, anche se questo introduce un'asimmetria
rispetto ai tipi fondamentali, i cui oggetti temporanei non sono mai
modificabili.
Ciao,
Nicola

 tale non puo' essere legato ad un reference non const.
Qualche riferimento allo standard please.
Io l'ho sempre pensata come Andrea.
--
Andrea Ferro
---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info 
http://www.brainbench.com/transcript.jsp?pid=2522556

Andrea
Mi associo..
~Guybrush~

tale non puo' essere legato ad un reference non const.
...
Credo che il BCC55 abbia qualche problema sulla cosa... con codice
che mi sembra equivalente il comportamento e' diverso:
   struct X
   {
     X(int){} // Conversione implicita int->X
   };
   void f(X&) // Parametro non-const
   {
   }
   int main()
   {
     f(X(0)); // Nessun warning
     f(0);    // Warning (giustamente)
     return 0;
   }
Anche in passato mi sono scontrato con qualche problema di
BCC55 e quel tipo di warning. In codice in cui una funzione
accettava un const reference mi sono comunque beccato quella
segnalazione (priva di senso).
Avevo a suo tempo anche segnalato la cosa a Borland.
Secondo me la chiamata f(X(0)) non dovrebbe generare warning.
Quella istanza e' tecnicamente definita una "temporanea"
dallo standard ?


modificabili.
cioè, in pratica:
struct S {
  void f();
}
void g(S);
int main() {
  S().f(); // è permesso
  g(S()); // non è permesso
}
ho capito bene?
--
Antonio Cuni, 18 anni, studente
Brainbench Certification: 
Master C++: 4.6, higher than 98% of all previous test takers.
C Language: 3.32, higher than 79% of all previous test takers.
http://www.brainbench.com/transcript.jsp?pid=1301688

 
Si parlava di riferimenti non-const, quindi:
 void g( S& );
Max


 ho capito bene?
Salvo che dovrebbe essere "void g(S&);", hai capito quello che sostiene Nicola.
Ma io e il mio omonimo (Andrera Griffini) non siamo dello stesso parere.
Infatti ho chiesto a Nicola se puo' dare un riferimento allo standard. Forse ha
ragione. Ma se e' cosi' allora ho (anzi abbiamo) imparato qualche cosa di nuovo.


gcc 2.95.2 non accetta il tuo esempio per il problema che ho indicato.
Ho chiesto su c.l.c++.m e sto aspettando risposta.
Ciao,
Nicola

 wrote in message
news:3ACC4E1F...@divalsim.it...

Nicola, potresti provare a compilarci questo e dirmi che fa il gcc?
struct S {
 int i;
};
struct B {
 S s;
 operator S&(){ return s; }
};
struct D: B, S {
 D(){i=1; s.i=2;}
};
void f(S&s)
{
 std::cout << s.i; << std::endl;
}
int main()
{
 f(D());
}
--


 dallo standard ?
Tecnicamente dato che il codice non compila, non esiste nessuna istanza.
Il problema e' subdolo ma ha a che fare con il reference binding. Una reference
non costante puo' essere legata solo ad un lvalue. Ma l'invocazione esplicita di
un costruttore corrisponde ad un rvalue. Ergo non dovrebbe compilare.




In effetti ho provato anche io e ho ottenuto un messaggio
che mi ha insospettito in quanto parlava del passaggio di
un r-value come reference.
Spulciando i documenti in mio possesso ho visto che questo
e' quanto viene ribadito in piu' occasioni (una istanza
costruita in mezzo ad una espressione -anche esplicitamente- 
e' un r-value e non un l-value).
Credo che dietro questa decisione ci siano motivazioni
precise (a me ignote); resta il fatto che a questo
punto mi sembra di capire che:
  struct X
  {
    X(int)  { }
    X& me() { return *this; }
  };
  void f(X&)
  {
  }
  int main()
  {
    f(X(0));       // Vietato
    f(X(0).me());  // Ammesso
    return 0;
  }
BCC55 accetta senza problemi entrambe le righe, mentre
gcc segnala un warning sulla prima.
Mi scuso se il mio intervento ha aumentato la confusione
su questa cosa. Ho provato a capire cosa ci stia dietro
queste apparenti incongruenze senza riuscirci.
Ad esempio anche sugli operatori di conversione esistono
strani casi particolari; se definissi, per assurdo, un
operatore in grado di convertire una istanza in un
reference alla stessa questo non sarebbe comunque in
grado di risolvere il problema:
  struct X
  {
    X(int)        { }
    operator X&() { return *this; }
  };
visto che esiste una regola esplicita che dice che questo
operatore di conversione non verrebbe invocato in questo
caso. Senza questa regola questo trucchetto potrebbe
essere usato per trasformare implicitamente un r-value
non const di tipo class in un l-value). Operatori di
questo tipo *possono* pero' essere chiamati in altri
casi (se virtuali).
Boh... non credo che i membri del comitato siano dei pazzi
furiosi e quindi avranno avuto le loro ragioni.
Non comprendo ma mi adeguo :) ... secondo me era piu'
"pulito" permettere la conversione implicita rvalue->lvalue
nel caso di istanze e rendere istanze *const* quelle create
implicitamente dal compilatore per le conversioni.
Ad esempio non capisco anche perche' e' stato (faticosamente)
vietato "f(X(...))" e invece e' permesso "X(...).x=0;" dove
x e' un membro dati della classe X...
Cosa c'e' di brutto nella prima espressione che invece non
e' presente nella seconda ?


un costruttore corrisponde ad un rvalue. Ergo non dovrebbe compilare.
In effetti controllando i documenti relativi allo standard
ho trovato la stessa cosa.
Viene invece compilato senza problemi da gcc il seguente...
   struct X
   {
     int x;
     X(int) { }
   };
   int main()
   {
     X(0).x=0;
     return 0;
   }
pero' se scrivo
   int& x=X(0).x;
ottengo un warning che mi avvisa che sto inizializzando un reference
a intero con un r-value intero. Boh... ma se "X(0).x" e' un r-value
intero perche' posso assegnargli un valore ?
Quello che non capisco non e' la segnalazione (utilissima in questo
caso... X(0).x smette di esistere al ";" - credo :) ) ma la
giustificazione che la provoca che invece mi paiono oscuri.


 intero perche' posso assegnargli un valore ?
Proprio perché è un r-value, no? Altrimenti cosa intendi per r-value?
Fosse un r-value costante allora avresti ragione ma non mi pare lo sia,
o no? Io per r-value intendo proprio qualcosa che può comparire a destra
di un'assegnazione.
EMG


Non capisco la tua risposta. Andrea si chiede, mi pare, come possa
"X(0).x" essere un r-value se è consentito assegnargli un valore:
  X(0).x = 0;
Max


E' che quando uno è rincoglionito, che ci vuoi fare? Ho confuso r-value
con l-value... :-(
EMG

 void g( S& );
si, è vero, mi è rimasto nella tastiera...


 wrote in message
news:3acd5fe4...@news.interbusiness.it...

Il diagnostico che ricevi e' corretto. Quello che non e' corretto e' che non
rivevi un diagnostico nel main. Ovvero:
  X(0).x=0;
non dovrebbe compilare in quanto X(0).x e' un rvalue e l'operator= richiede un
"modifiable lvalue". Questo stando allo standard.
Riguardo alla lifetime dell'oggetto che hai creato, salvo che in teoria se
l'espressione non compila non esiste nessun oggetto, hai ragione ... ma a metą.
Per esempio guarda questo codice (che dovrebbe compilare):
struct B {
    virtual int f() const { return 1; }
};
struct X: B {
  int x;
  X():x(2){}
  virtual int f() const { return x; }
};
int main()
{
  const int& r=X().x;
  const B& b=X();
}
In questo caso la reference e' costante, quindi e' legale assegnarla ad una
copia di un rvalue. L'oggetto di tipo X viene "distrutto al ;". Ma prima un
oggetto di tipo int (o const int) viene creato e gli viene assegnato il valore
di x (ovvero 2). Questo oggetto sopravvive fino al temine di main (ovvero dello
scope della reference).
Il secondo caso e' ancora piu' interessante. La reference e' a un B costante e
l'espressione che la assegna e' un rvalue di tipo X. Il compilatore ha due
scelte: o mantiene l'intero oggetto "vivo" fino al termine di main (e lega la
reference ad esso) o ne fa una copia. Ma la copia non e' semplicemente una copia
del sottooggetto B, bensi' dell'intero oggetto X (verificabile chiamando il
metodo virtuale). In questo caso l'oggetto X creato potrebbe anche non "morire
al ;".


 e' presente nella seconda ?
Nulla. Infatti anche la seconda non dovrebbe compilare!


al ;".
Questo e' effettivamente interessante. Sarebbe anche interessante
scoprire quanti sono i compilatori che usano questa regola.
Io ho visto che con il seguente programma:
  #include <iostream>
  struct X
  {
    X() { std::cout << "X()\n"; }
    X(const X&) { std::cout << "X(const X&)\n"; }
    virtual int f() const { return 1; }
    virtual ~X() { std::cout << "~X()\n"; }
  };
  struct Y : X
  {
    Y() { std::cout << "Y()\n"; }
    Y(const Y&) { std::cout << "Y(const Y&)\n"; }
    int f() const { return 2; }
    ~Y() { std::cout << "~Y()\n"; }
  };
  int main()
  {
    const X& x=Y();
    std::cout << x.f() << std::endl;
    return 0;
  }
Sia g++ che BCC55 che Visual C++ 6.0(SP3) generano come output
  X()
  Y()
  2
  ~Y()
  ~X()
e quindi non vengono fatte copie di Y o del sotto-oggetto X
e (come ovvia conseguenza) la f() chiamata e' quella definita
in Y(). BCC55 e Visual C++ compilano senza warning se viene
rimosso il "const" nella dichiarazione del reference e questo
e' in disaccordo con quanto dice lo standard.


 e' in disaccordo con quanto dice lo standard.
Infatti. Non dovrebbero compilare.


 e' in disaccordo con quanto dice lo standard.
Nel caso di VC non e' vero. O meglio: e' vero se lasci abilitate le estensioni
al linguaggio (/Ze) ma non se le disabiliti (/Za) nel qual caso ti da,
giustamente, un diagnostico (puoi anche dare un'occhiata ai warning di livello
4).


4).
Quando uso VC tendo a lasciare il warning level a 3 (io per indole
lo porterei anche a 4 ma purtroppo come sicuramente sai ci sono
problemi con windows.h).
Questo, unitamente ad altri piccole chicche (accenniamo ad esempio
allo scope delle dichiarazioni interne a for(...), al necessario
pragma warning disable 4786, ai probemi con container standard e
DLL - risolti per i container MFC(!) ) la dice secondo me lunga
su quanto MS sia interessata al C++ standard.


 su quanto MS sia interessata al C++ standard.
Mettendo i puntini sulle i dobbiamo dire che:
1) il VC6 e' parecchio vecchio (piu' dello standard stesso!)
2) le librerie specifiche Windows sono ancora piu' vecchie e talmente complesse
che e' piu' difficile correggere quelle che non il compilatore! Ergo se vuoi un
compilatore conforme dimenticati ATL, MFC e persino windows.h (almeno ancora per
un bel pezzo).
3) VC7 e' parecchio meglio di VC6 anche in senso ISO, nonostante la maggior
parte dello sforzo non sia in direzione della conformancy.
4) il lavoro che separa VC7 da VC8 e' praticamente quasi tutto in direzione ISO.
Ergo non solo VC7 e' molto meglio di VC6 ma ora che .NET e' supportato tutto lo
sforzo andrà in direzione di ISO e vedremo dei buoni risultati nell'arco di un
anno o due (ma gli anni di MS possono durare piu' di 365 gg).


 Nicola, potresti provare a compilarci questo e dirmi che fa il gcc?
[...]
Ref2.cpp: In function `int main(int, char **)':
 f(D());
Ref2.cpp:27: initialization of non-const reference type `struct S &'
Ref2.cpp:27: from rvalue of type `D'
void f(S&s)
Ref2.cpp:20: in passing argument 1 of `f(S &)'
Nel frattempo ho anche avuto conferma dei miei sospetti su c.l.c++.m .
Ciao,
Nicola
-- 
Posted from 
mail.divalsim.it
 [194.185.113.194] 
via Mailgate.ORG Server - 
http://www.Mailgate.ORG

   }
Penso che si tratti di decisioni su argomenti apparentemente diversi
prese in momenti diversi senza considerare con attenzione le
interazioni. Le decisioni sono:
- Non e' possibile inizializzare un reference non const
  con un valore temporaneo.
Penso che il razionale sia che lo scopo del reference non const e'
*permettere* effetti collaterali, quindi un costrutto volto a perdere
tali effetti e' da considerarsi un probabile errore.
- E' possibile chiamare metodi non const su valori temporanei
Dopo tutto chiamare a.f() equivale a f(a) e scritture tipo f(g(h(a)))
sono da sempre consentite non solo in C, ma in molti altri linguaggi.
Peccato che la giustapposizione di queste regole provochi delle
inconsistenze.
Un esempio abbastanza tipico, riferito in realta' alla sola seconda
regola, e' il seguente:
vector<int> v;
v.push_back(1);
v.push_back(2);
vector<int>::iterator i = ++v.begin();
Quest'ultima istruzione non e' valida se vector<int>::iterator e' un
puntatore, mentre lo e' se vector<int>::iterator e' un'istanza di una
classe.
Se non ricordo male il comitato nazionale aveva suggerito in sede di
ratifica che la seconda regola menzionata sopra fosse rimossa.



Ho il sospetto che sbagli (cosa che vale anche per il VC7). Vedi 8.5.3 sub 5 e
dimmi che ne pensi.


 dimmi che ne pensi.
Penso che valga il caso dove dice: "— Otherwise, the reference shall be
to a non-volatile const type (i.e., cv1 shall be const)."
Ciao,
Nicola

 to a non-volatile const type (i.e., cv1 shall be const)."
In tal caso ti chiedo esplicitamente perche' non vale il caso:
- has a class type (i.e., T2 is a class type) and can be implicitly converted to
an lvalue of type "cv3 T3," where "cv1 T1" is reference-compatible with "cv3 T3"
{This requires a conversion function (12.3.2) returning a reference type.} (this
conversion is selected by enumerating the applicable conversion functions
(13.3.1.6) and choosing the best one through overload resolution (13.3))
soprattutto considerando che 13.3.1.6 sembra combaciare benissimo con il nostro
caso!


Scusa, non avevo letto bene il tuo codice. A naso dovrebbe compilare
correttamente.
Ciao,
Nicola