2013-04-05 2 views
6

Le code est le suivant:La fonction C++ renvoie un rvalue, mais une nouvelle valeur peut-elle lui être affectée?

#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; 
} 

Le compilateur g ++ donne l'erreur suivante:

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&)’ 

Il dit que je ne peux pas passer un rvalue comme argument d'une référence non-const, mais ce que je suis confus est de savoir pourquoi je peux attribuer à cette valeur, comme le montre le code.

+5

Oui, les références non const ne peuvent pas être liées à des temporaires. – chris

+0

Étrange. Je peux compiler et exécuter ceci dans MS VS 2010. – Kupto

+0

@chris mais pourquoi je peux l'assigner. Je peux l'assigner, pourquoi je ne peux pas le passer comme référence? – haipeng31

Répondre

8

Passer le rvalue rtByValue() à une fonction qui attend une référence lvalue ne fonctionne pas parce que cela nécessiterait l'argument de référence lvalue à initialiser à partir d'un rvalue. §8.5.3/5 décrit comment les références lvalue peut être initialisé – Je ne citerai pas en entier, mais il dit essentiellement qu'une référence lvalue peut être initialisé

  • soit d'une autre référence lvalue
  • ou quelque chose qui peut être converti en une référence lvalue d'un type intermédiaire
  • ou d'un rvalue, mais seulement si la référence lvalue initialisant est un const référence

depuis l'argument que nous devons initialiser est pas un const -référence, rien de tout cela pplies.

D'autre part,

rtByValue() = aa; 

-à-dire, l'affectation à un objet temporaire, est possible en raison de:

(§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 ]

Donc cela ne fonctionne que parce que A est de type de classe, et (implicitement défini) L'opérateur d'affectation est une fonction membre. (Voir this related question pour plus de détails.)

(Donc, si rtByValue() devait revenir, par exemple, un int, l'affectation ne fonctionnerait pas.)

0

@ddriver Cette sortie produit le numéro 7, comme je m'attendais.

#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

Ce code compile pour vous aussi? – 0x499602D2

+0

yope dans VS 2010 – Kupto

+0

Juste parce que vous obtenez la valeur ne signifie pas que ce n'est pas un comportement indéfini. J'ai obtenu les bonnes valeurs d'un index hors limites d'un tableau, qui tombe dans la même catégorie. Je ne pense pas que la norme permette cela, MS pourrait avoir des points de vue différents à ce sujet, comme ils le font dans de nombreux autres aspects. – dtech

1

Parce que vous pouvez (mais ne devrait pas!) override operator = tel que l'appeler sur un rvalue a du sens. Considérez le code suivant:

#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; 
} 

Maintenant Compilons il quelques façons:

$ 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 

Je ne peux pas promettre que vous verrez les mêmes résultats.

Comme vous pouvez le voir, l'affectation se produit, mais toute tentative d'utilisation de l'objet affecté a un comportement spécifique à l'implémentation.

Par ailleurs, ce seulement s'applique aux types définis par l'utilisateur. Ce code:

int v(){ 
    return 2; 
} 

main(){ 
    v()=4; 
} 

ne compile pas.

+0

Ce n'est pas spécifique à l'implémentation, c'est * un comportement non défini *. Vous accédez à un objet temporaire détruit. –

Questions connexes