9

The code is as follows:

 #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++ compiler gives the following error:

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

It says that I can't pass an rvalue as an argument of a non-const reference, but what I'm confused about is why I can assign to this rvalue, just as the code shows.

jogojapan
  • 68,383
  • 11
  • 101
  • 131
haipeng31
  • 635
  • 1
  • 7
  • 16
  • 5
    Yes, non-const references cannot bind to temporaries. – chris Apr 05 '13 at 02:05
  • Strange. I can compile and run this in MS VS 2010. – Kupto Apr 05 '13 at 02:08
  • @chris but why i can assign to it.I can assign to it, why i can't pass it as a reference? – haipeng31 Apr 05 '13 at 02:09
  • What compiler are you using? – David G Apr 05 '13 at 02:10
  • @0x499602D2 he uses g++ and I am VS 2010 – Kupto Apr 05 '13 at 02:10
  • @Kupto Very strange - I get the same but it seems wrong. See also: http://stackoverflow.com/questions/8293426/error-invalid-initialization-of-non-const-reference-of-type-int-from-an-rval – Keith Apr 05 '13 at 02:11
  • @Kupto maybe the stardard don't say something about this.It's quite strange – haipeng31 Apr 05 '13 at 02:12
  • @Kupto - but does it run up to specification or is it undefined behavior? – dtech Apr 05 '13 at 02:12
  • I seem to recall from `Design and Evolution of C++` that Stroustrup deliberately wanted to trap the case giving the error as it was so easy to do by accident. – Keith Apr 05 '13 at 02:13
  • It's fine to assign to it because it returns by value. You get a copy of what it returns. That copy can be assigned to, even if it is pointless. – chris Apr 05 '13 at 02:25
  • It seems you can, but it is pointless to do so: [link](http://stackoverflow.com/questions/2216889/assign-a-value-to-a-function). – gibertoni Apr 05 '13 at 02:26
  • I think maybe the compiler should forbid “rtByValue() = aa; ” to pass the syntax analysis – haipeng31 Apr 05 '13 at 02:33
  • @user1679133: There's nothing wrong with the *syntax*. The semantics are highly questionable, but you're allowed to invoke a non-`const` member function on a temporary, and `rtByValue() = aa;` is exactly that -- a member function invocation on the temporary object. – Ben Voigt Apr 05 '13 at 03:04
  • @BenVoigt If a function return a basic type such as 'int',I can't assign to it. I wonder if this is an inconsistence in syntax...(int can't,but user-defined type can).Just a ridiculous thought – haipeng31 Apr 05 '13 at 03:09
  • @user1679133: `(1+2) = 4;` doesn't involve a member function invocation, because `int` doesn't have an `operator=` member function. You need to stop talking about *syntax* though, the syntax is fine. The semantics are inconsistent on first glance, but when you recognize the equivalence of assigning a class instance with a member function invocation, you can then understand why the behavior is the way it is. – Ben Voigt Apr 05 '13 at 03:10

3 Answers3

10

Passing the rvalue rtByValue() to a function that expects an lvalue reference doesn't work because this would require the lvalue reference argument to be initialized from an rvalue. §8.5.3/5 describes how lvalue references can be initialized – I won't quote it in full, but it basically says that an lvalue reference can be initialized

  • either from another lvalue reference
  • or something that can be converted to an lvalue reference of an intermediary type
  • or from an rvalue, but only if the lvalue reference we initialize is a const-reference

Since the argument we need to initialize is not a const-reference, none of this applies.

On the other hand,

rtByValue() = aa; 

i.e., assigning to a temporary object, is possible because of:

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

So this works only because A is of class-type, and the (implicitly defined) assignment operator is a member function. (See this related question for further details.)

(So, if rtByValue() were to return, for example, an int, then the assignment wouldn't work.)

Community
  • 1
  • 1
jogojapan
  • 68,383
  • 11
  • 101
  • 131
2

Because you can (but shouldn't!) override operator= such that calling it on an rvalue makes sense. Consider the following code:

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

Now let's compile it a few ways:

$ 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

I can't promise you'll see the same results.

As you can see, the assignment happens, but any attempt to use the object that got assigned results in implementation-specific behaviour.

Incidentaly, this only applies to user-defined types. This code:

int v(){
  return 2;
}

main(){
  v()=4;
}

doesn't compile.

dspeyer
  • 2,904
  • 1
  • 18
  • 24
  • It's not implementation-specific, it's *undefined behavior*. You're accessing a destroyed temporary object. – Ben Voigt Apr 05 '13 at 04:53
0

@ddriver This outputs number 7, as I would expect.

#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;
 }
Kupto
  • 2,802
  • 2
  • 13
  • 16
  • Just because you get the value doesn't mean it is not undefined behavior. I've been getting the right values from an out of bounds index of an array, which falls in the same category. I don't think the standard allows that, MS might have different views on this, like they do in many other aspects. – dtech Apr 05 '13 at 02:20
  • I can't see why should it not work. Actual instance of class A is being made and is living while being passed be reference, so there should be no problem. I have no problem to assign to `i` within the `passByRef` function. – Kupto Apr 05 '13 at 02:21
  • 5
    This is a well-known compiler bug in Visual C++, which will probably never be fixed. – Ben Voigt Apr 05 '13 at 02:24
  • @BenVoigt Just out of curiosity. Why is it considered to be a bug? I understand that there is a being constructed reference from temporary variable, but why couldn't it be? Of course it gives you an easy option to shoot yourself in the leg, but isn't `c/c++` all about being able to do this? – Kupto Apr 05 '13 at 02:28
  • 3
    It is a bug because the Standard requires the compiler to generate a diagnostic, and Visual C++ does not. – Ben Voigt Apr 05 '13 at 02:29
  • Well, it works with MSVC2012 as well, so I guess it is still not fixed. – dtech Apr 05 '13 at 02:36
  • 2
    What would you do with it anyway? Making it a const reference works fine, and if you need to modify it, you can always take it by value or rvalue reference. Modifying the original is pointless because the original is a temporary that's about to be destroyed. – chris Apr 05 '13 at 02:37
  • @BenVoigt now this is really polemic, but what so horrible could you do with this or what could happened, that it made them put this limitation into standard? – Kupto Apr 05 '13 at 02:40
  • @chris you could be passing reference to `this` to some init function for example. It is expecting that `this` is not temporary instance, that has method returning reference to itself. – Kupto Apr 05 '13 at 02:47