2013-04-05 7 views
6

Il codice è il seguente:La funzione C++ restituisce un valore, ma a questo può essere assegnato un nuovo valore?

#include <iostream> 
using namespace std; 

class A { 

}; 

A rtByValue() { 
return A(); 
} 

void passByRef(A &aRef) { 
    // do nothing 
} 

int main() { 
    A aa; 
    rtByValue() = aa;   // compile without errors 
    passByRef(rtByValue());  // compile with error 

    return 0; 
} 

The g ++ compilatore genera il seguente errore:

d.cpp: In function ‘int main()’: 
d.cpp:19:23: error: invalid initialization of non-const reference of type ‘A&’ from an rvalue of type ‘A’ 
d.cpp:12:6: error: in passing argument 1 of ‘void passByRef(A&)’ 

Si dice che non posso passare un rvalue come argomento di un riferimento non-const, ma quello di cui sono confuso è il motivo per cui posso assegnare a questo valore, proprio come mostra il codice.

+5

Sì, i riferimenti non const non possono essere associati ai provvisori. – chris

+0

Strano. Posso compilare ed eseguire questo in MS VS 2010. – Kupto

+0

@chris ma perché posso assegnarlo. Posso assegnarlo, perché non posso passarlo come riferimento? – haipeng31

risposta

8

Passando rvalue rtByValue() ad una funzione che prevede un riferimento lvalue non funziona perché ciò richiederebbe l'argomento riferimento lvalue da inizializzare da un rvalue. §8.5.3/5 descrive come riferimenti lvalue può essere inizializzato – non citerò in pieno, ma si dice in sostanza che un riferimento lvalue può essere inizializzato

  • sia da un altro riferimento lvalue
  • o qualcosa che può essere convertito ad un riferimento lvalue di tipo intermedio
  • o da un rvalue, ma solo se il riferimento Ivalue inizializziamo è un const riferimento

Poiché l'argomento dobbiamo inizializzare non è un const - riferimento, niente di questo a pplies.

D'altra parte,

rtByValue() = aa; 

cioè, assegnando a un oggetto temporaneo, è possibile a causa di:

(§3.10/5) An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [ Example: a member function called for an object (9.3) can modify the object. — end example ]

Quindi questo funziona solo perché A è di classe-tipo, e la (implicitamente definito) l'operatore di assegnazione è una funzione membro. (Vedere this related question per maggiori dettagli.)

(Quindi, se rtByValue() dovessimo tornare, per esempio, un int, quindi l'assegnazione non avrebbe funzionato.)

0

@ddriver Questo numero di uscite 7, come mi aspetterei.

#include <iostream> 
using namespace std; 

class A { 
public: 
    int i; 
    A() {i = 0x07;} 
}; 

A rtByValue() { 
return A(); 
} 

void passByRef(A &aRef) { 
    cout << aRef.i; 
} 

int main() { 
    passByRef(rtByValue()); 
    return 0; 
} 
+0

Questo codice viene compilato anche per te? – 0x499602D2

+0

yope in VS 2010 – Kupto

+0

Solo perché si ottiene il valore non significa che non è un comportamento non definito. Ho ottenuto i valori corretti da un indice fuori limite di un array, che rientra nella stessa categoria. Non credo che lo standard lo consenta, MS potrebbe avere opinioni diverse su questo, come fanno in molti altri aspetti. – dtech

1

Perché si può (ma non dovrebbe!) override operator = tale che la chiamata su un valore razionale abbia senso. Si consideri il seguente codice:

#include<iostream> 

using namespace std; 

class foo; 

foo* gotAssigned = NULL; 
int assignedto = -1; 

class foo { 
public: 
    foo(int v) : val(v) {} 
    foo& operator=(int v) { 
    assignedto=v; 
    gotAssigned = this; 
    val = v; 
    return *this; 
    } 
    int val; 
}; 

foo theFoo(2); 

foo returnTheFooByValue() { 
    return theFoo; 
} 

main() { 
    returnTheFooByValue()=5; 
    cout << "[" << assignedto << "] " << theFoo.val << " versus " << gotAssigned->val << endl; 
} 

Ora cerchiamo di compilare un paio di modi:

$ g++ -O0 -o rveq rveq.cc && ./rveq 
[5] 2 versus 5 
$ g++ -O1 -o rveq rveq.cc && ./rveq 
[5] 2 versus 2 
$ g++ -O4 -o rveq rveq.cc && ./rveq 
[5] 2 versus -1218482176 

non posso promettere vedrete gli stessi risultati.

Come si può vedere, l'assegnazione avviene, ma qualsiasi tentativo di utilizzare l'oggetto a cui sono stati assegnati risultati ha come risultato un comportamento specifico dell'implementazione.

In caso di emergenza, questo solo si applica ai tipi definiti dall'utente. Questo codice:

int v(){ 
    return 2; 
} 

main(){ 
    v()=4; 
} 

non viene compilato.

+0

Non è specifico dell'implementazione, è * comportamento non definito *. Stai accedendo a un oggetto temporaneo distrutto. –

Problemi correlati