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 è?

È stato utile?

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

enter image description here

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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top