2016-02-13 21 views
7
class Test { 

public: 

    int n1; 

}; 

Test func() { 

    return Test(); 

} 

int main() { 

    func() = Test(); 

} 

Questo non ha senso per me. Come e perché è permesso? È un comportamento indefinito? Se una funzione restituisce un valore, allora come è possibile impostare un rvalue su un altro valore? Se ho provato questo con qualsiasi tipo primitivo, mi darebbe un errore come mi aspetto.Assegnazione Rvalore restituito dalla funzione a un altro Rvalore

So che i lvalues ​​sono un posto in memoria, quindi la funzione crea un lvalue temporaneo (rvalue?) E lo assegna a un altro lvalue? Qualcuno può spiegare come funziona questa sintassi?

+0

"_Se una funzione restituisce un rvalue_" E non lo fa. – curiousguy

risposta

6

La categoria di valore dell'espressione di chiamata di funzione è in effetti un valore.

In effetti, non è possibile chiamare l'operatore di assegnazione copia su primitive di valore. La definizione storica di rvalues ​​in C era in realtà la distinzione che essi non possono essere sul lato sinistro di un asserzione.

Gli operatori di assegnazione delle classi sono leggermente diversi. Sono funzioni membro regolari. E nessuna regola impedisce di chiamare funzioni membro di rvalue. In effetti, è spesso abbastanza utile quando le funzioni hanno effetti collaterali.

Come funziona, beh, l'operatore di assegnazione copia viene chiamato sul temporaneo, l'operatore copia l'argomento della mano destra, modificando lo stato del temporaneo. Dopo l'istruzione, l'oggetto temporaneo viene scartato. Non c'è UB, solo una copia inutile.

È possibile evitare di chiamare l'assegnazione copia su un rvalue, dichiarando l'operatore con un qualificatore di riferimento come questo: Test& operator=(Test) & = default;. I qualificatori di riferimento sono stati aggiunti solo più tardi in C++ 11, quindi non è stato possibile specificare (implicito) l'assegnazione della copia per essere qualificata per il ref-in prima. Presumibilmente, C++ 11 non ha modificato il qualificatore del costruttore di copie implicite per evitare di rompere il vecchio codice che assegna a un valore rvalore, anche se tale assegnazione sembra abbastanza inutile. Il High Integrity C++ Coding Standard consiglia di utilizzare il qualificatore di riferimento con operatori di assegnazione copia definiti dall'utente.

+0

"_Il valore di ritorno della funzione è in effetti un valore rvalore._" No, l'oggetto restituito è un oggetto. – curiousguy

+1

Le espressioni oggetto @curiousguy hanno categorie di valori. La categoria è il valore r in questo caso. – user2079303

+0

Tutte le espressioni hanno categorie, non solo quelle che si riferiscono a un oggetto. L'oggetto restituito è solo un oggetto, non un'espressione. – curiousguy

1

C'è una curva di apprendimento piuttosto ripida fino a value categories (almeno per me), ma credo che tu abbia il terminology proprio nell'esempio. Così

func() 

restituisce infatti un prvalue e dal C++ standard par. 3.10.5 (ho solo la corrente draft, il tuo numero di punto possono variare) leggiamo:

valore assegnabile per un oggetto è necessaria al fine di modificare l'oggetto, tranne che un rvalue di tipo classe può anche essere usato per modificare il suo referente in determinate circostanze. [Esempio: una funzione membro chiamata per un oggetto (9.3) può modificare l'oggetto. - esempio fine]

Quindi l'operatore di assegnazione, che è una funzione membro, come nell'esempio menzionato nello standard è un'eccezione alla regola, permettendo rvalues ​​da modificare.

Questo ha generato molte critiche nel mondo della programmazione, con il suo esempio più estremo questo C++ FQA estratto:

(sì, trappole mortali rivestite di zucchero, voi cheerleaders incompetenti.X& obj=a.b().c() - oops, b() è un oggetto temporaneo e c() restituisce un riferimento in esso! Non dovrebbe averlo assegnato a un riferimento. . Non molte possibilità per un avviso del compilatore, o)

Ma in real C++ di programmazione ha industrial applications come il named parameter linguaggio:

std::cout << X::create().setA(10).setB('Z') << std::endl; 
+0

Hai sbagliato la terminologia: la funzione chiamata 'func()' è un valore, il valore restituito non lo è. – curiousguy