È un membro di un rvalue struttura di un rvalue o lvalue?
Domanda
Una chiamata di funzione restituzione di una struttura è un rvalue espressione, ma che cosa circa i suoi membri?
Questo pezzo di codice funziona bene con il mio compilatore g++, ma gcc dà un errore che dice "lvalue richiesto come operando di sinistra di assegnazione":
struct A
{
int v;
};
struct A fun()
{
struct A tmp;
return tmp;
}
int main()
{
fun().v = 1;
}
gcc tratta fun().v
come rvalue, e posso capire che.
Ma g++ non pensare che l'espressione di assegnazione è sbagliato.Questo significa che fun1().v è lvalue in C++?
Ora il problema è, ho cercato il C++98/03 standard, non trovando nulla da raccontare se fun().v
è lvalue o rvalue.
Così, che cosa è?
Soluzione
Un membro di un'espressione rvalue è rvalue.
La norma in 5.3.5 [expr.ref]:
Se E2 viene dichiarato di avere tipo “Riferimento alla T”, allora è un E1.E2 lvalue [...] - Se E2 è un membro di dati non statico, e il tipo di E1 è “CQ1 VQ1 X”, e il tipo di E2 è “vq2 CQ2 T”, il espressione designa il membro nominato dell'oggetto designati per la prima espressione. Se E1 è un lvalue, poi E1.E2 è un lvalue.
Altri suggerimenti
Edit:Ok, credo di avere finalmente qualcosa da standard:
Nota che v
è di tipo int
che ha un built-in operatore di assegnazione:
13.3.1.2 Operatori nelle espressioni
4 Per il built-in operatori di assegnazione, le conversioni dell'operando di sinistra sono limitate come segue:— no provvisori vengono introdotti per tenere l'operando di sinistra, e [...]
fun1()
dovrebbe restituire un riferimento.Un non-reference/puntatore tipo di ritorno di una funzione è un r-valore.
3.10 Lvalues e rvalues
5 Il risultato di una chiamata a una funzione che non restituisce un lvalue di riferimento è un rvalue [...]
Protettori così, fun1().v
è un rvalue.
8.3.2 Riferimenti
2 Un tipo di riferimento che viene dichiarato utilizzando & è chiamato un lvalue di riferimento, e un tipo di riferimento che viene dichiarato utilizzando && viene chiamato un rvalue di riferimento.Lvalue i riferimenti e rvalue riferimenti sono tipi distinti.
Questo è un buon momento per imparare che cosa xvalues un glvalues sono.
Rvalues può essere di due tipi - prvalues e xvalues.Secondo il nuovo C++17 standard
Un prvalue è un'espressione la cui valutazione inizializza un oggetto, campo di bit, o operando di un operatore, come specificato dal contesto in cui appare.
quindi qualcosa di simile fun()
nel tuo esempio restituisce un prvalue (che è un rvalue).Questo ci dice anche che fun().v
non è un prvalue, dal momento che non è una vaniglia di inizializzazione.
Xvalues che sono anche rvalues sono definiti come
Un xvalue (una "scadenza" valore), inoltre, si riferisce a un oggetto, di solito vicino alla fine del suo ciclo di vita (in modo che le sue risorse possono essere spostati, per esempio).Alcuni tipi di espressioni che coinvolgono riferimenti rvalue (8.3.2) rendimento xvalues.[ Esempio:Il risultato della chiamata di una funzione il cui tipo di ritorno è un rvalue riferimento ad un tipo di oggetto è un xvalue (5.2.2).- esempio ]
Oltre a rvalues, un altro ombrello categoria valore è un glvalue che essere di due tipi xvalues e il tradizionale lvalues.
Abbiamo a questo punto definito il valore essenziale categorie.Questo può essere visualizzato come
Categoria glvalue può essere ampiamente pensato di dire, che cosa lvalues dovevano dire prima di spostare la semantica è diventata una cosa - una cosa che può essere sul lato sinistro di un'espressione. glvalue significa generalizzata lvalue.
Se guardiamo la definizione di un xvalue, poi si dice che qualcosa è un' xvalue se è vicino alla fine della sua vita.Nel tuo esempio, fun().v
è vicino alla fine del suo ciclo di vita.Così le sue risorse possono essere spostati.E dal momento che le sue risorse possono essere spostati non è un lvalue, quindi la tua espressione si inserisce nel solo foglia categoria valore che rimane - un xvalue.
Ho notato che il gcc tende ad avere molto pochi compunctions sull'utilizzo di rvalues come lvalues nell'assegnazione di espressioni.Questo, per esempio, raccoglie bene:
class A {
};
extern A f();
void g()
{
A myA;
f() = myA;
}
Perché è legale, e questo non è (cioènon compilare) anche se in realtà mi confonde:
extern int f();
void g()
{
f() = 5;
}
IMHO, il comitato standard ha qualche spiegazione per quanto riguarda lvalues, rvalues e in cui possono essere utilizzati.È uno dei motivi per cui sono così interessato a questa domanda su rvalues.
Si diventa evidente se si considera che il compilatore genererà un costruttore di default, un costruttore di copia di default, e un operatore di assegnazione di copia di default per voi, nel caso in cui la vostra struct / classe non contiene elementi di riferimento. Quindi, pensare che lo standard consente di chiamare i metodi membri su provvisori, cioè, è possibile chiamare i membri non-const su provvisori non const.
Vedere questo esempio:
struct Foo {};
Foo foo () {
return Foo();
}
struct Bar {
private:
Bar& operator = (Bar const &); // forbid
};
Bar bar () {
return Bar();
}
int main () {
foo() = Foo(); // okay, called operator=() on non-const temporarie
bar() = Bar(); // error, Bar::operator= is private
}
Se si scrive
struct Foo {};
const Foo foo () { // return a const value
return Foo();
}
int main () {
foo() = Foo(); // error
}
vale a dire. se si lascia la funzione foo () restituisce un const temporanea, poi si verifica un errore di compilazione.
Per rendere l'esempio completo, qui è come chiamare un membro di un temporarie const:
struct Foo {
int bar () const { return 0xFEED; }
int frob () { return 0xFEED; }
};
const Foo foo () {
return Foo();
}
int main () {
foo().bar(); // okay, called const member method
foo().frob(); // error, called non-const member of const temporary
}
Si potrebbe definire la durata di un temporaneo per essere all'interno dell'espressione corrente. E poi è per questo che è anche possibile modificare le variabili membro; se non si poteva, che la possibilità di poter chiamare i metodi membri non-const sarebbe portato ad absurdum.
modifica: Ed ecco le citazioni necessarie:
12.2 oggetti temporanei:
- 3) [...] oggetti temporanei vengono distrutti come ultimo passaggio nella valutazione pieno-espressione (1.9) che (lessicale) contiene il punto in cui sono stati creati. [...]
e poi (o meglio, prima)
3.10 lvalue e rvalues:
- 10) Un lvalue per un oggetto è necessaria per modificare l'oggetto eccetto che una rvalue di tipo classe può anche essere utilizzato per modificare il suo referente in determinate circostanze. [Esempio: una funzione membro chiamata per un oggetto (9.3) può modificare l'oggetto. ]
E un utilizzo esempio: http://en.wikibooks.org/ wiki / More_C% 2B% 2B_Idioms / Named_Parameter
È codice non ha alcuna scena. struttura restituita viene allocata sulla pila, quindi risultato assegnazione viene immediatamente sarà persa.
La vostra funzione dovrebbe Eiter allocare nuova istanza di A da:
new A()
In questo caso la firma migliore
A* f(){ ...
O tornare un'istanza esistente, ad esempio:
static A globalInstance;
A& f(){
return globalInstance;
}