2

When I run testRef() the results are not what I'd expect if a c++ reference really is a reference to the same point in memory:

struct MyPOD{
   float x;
};

struct Agg{
   MyPOD& m;

   Agg():m(MyPOD()){}
};

void testRef(){

    MyPOD p;
    p.x=15.5;

    Agg a;
    a.m=p; //referencing the object above

    qDebug()<<"a.m.x is: "<<a.m.x; //15.5

    qDebug()<<"p.x is: "<<p.x; //15.5

    a.m.x=5;

    qDebug()<<"a.m.x is: "<<a.m.x; //5

    qDebug()<<"p.x is: "<<p.x; //15.5
}

My question: If a.m is indeed a reference to p, why does changing a.m.x not also change p.x?

ilent2
  • 5,171
  • 3
  • 21
  • 30
johnbakers
  • 24,158
  • 24
  • 130
  • 258

3 Answers3

3

Your code doesn't do what you think it does. A reference is not "re-seatable". Once it is bound, you can't change what it is bound to.

The statement:

a.m=p;

does not make a.m reference p. It copies p into a.m (which stays bound as it was before).

Note that your constructor for Agg uses a non-standard extension. You can't bind a non-const l-value reference to a temporary in standard C++ - that would create a "dangling reference" since the temporary is destroyed right after the assignment. A const reference can extend the lifetime of the temporary (but not always).

Community
  • 1
  • 1
Mat
  • 202,337
  • 40
  • 393
  • 406
  • If it is a true copy, then how is this different than just leaving off the reference, which would also be a copy? I thought a reference was supposed to avoid overhead. – johnbakers Apr 26 '14 at 08:24
  • @OpenLearner: you do not know what a reference is, do you? It is referring to another thing, and a value based variable does not. When you change the reference here later, you would also change the original ... – László Papp Apr 26 '14 at 08:26
  • It's a copy-assignment. If you remove the reference in your aggregate, you have different semantics - `a.m` never references something else. It's not a matter of efficiency but a matter of semantics. Also your code is invalid, you can't bind the member to a temporary like you do in your constructor for `Agg`. – Mat Apr 26 '14 at 08:27
  • @Mat thanks, the code compiles and runs fine, so not sure about "invalid" but I'll agree based on this information it's certainly not "wise." – johnbakers Apr 26 '14 at 08:28
  • @OpenLearner: using MSVC by any chance? Both GCC and clang error out (correctly). Visual Studio has a non-standard extension that allows this kind of thing (I believe). – Mat Apr 26 '14 at 08:32
  • Yes I am using MSVC, good observation. I've noticed MSVC provides more than a few non-standard behaviors, which has always annoyed me. If a language is going to be strict, as C++ certainly is, it seems against the point of the tool to offer a compiler that is not strict. – johnbakers Apr 26 '14 at 08:34
  • switch to gcc or clang then :) – M.M Apr 26 '14 at 08:36
  • Wish I could; I'm connecting to third party dlls that are compiled in MSVC and I believe that requires I also use it. I tried Clang and it didn't recognize anything in those dlls – johnbakers Apr 26 '14 at 08:38
  • @OpenLearner You can [disable language extensions](http://msdn.microsoft.com/en-us/library/0k0w269d%28v=VS.90%29.aspx) with the `/Za` switch. – fredoverflow Apr 26 '14 at 09:13
2

When you write:

Agg():m(MyPOD())

this should fail to compile, because MyPOD() is a temporary object, and a temporary cannot be bound to a non-const reference.

If your compiler allows it, then who knows what is going on, you are in the land of your compiler extension. Perhaps it allows this code, but then the behaviour is undefined when you access m later and it refers to an object that has been destructed.

BTW the code a.m=p; calls a.m.operator=(p), it doesn't "re-seat the reference" or anything.

herohuyongtao
  • 49,413
  • 29
  • 133
  • 174
M.M
  • 138,810
  • 21
  • 208
  • 365
1

Putting my comment into an answer: references cannot be rebound, and it is bound in the constructor in your case. They have to be bound in the constructor initializer list, at least pre-c++11, and you are doing that over here:

Agg():m(MyPOD()){}
      ^^^^^^^^^^

What you seem to be working on later is a copy basically into that already bound variable which would also chang ethe original variable that it refers to, but in your case that is only MyPOD().

By the way, that is a temporary variable, so even if it seems to work OK currently, do not use it. There is no guarantee for it to remain working. It actually does not even compile with gcc 4.9 on my Archlinux giving this:

main.cpp: In constructor ‘Agg::Agg()’: main.cpp:12:19: error: invalid initialization of non-const reference of type ‘MyPOD&’ from an rvalue of type ‘MyPOD’ Agg():m(MyPOD()){}

or clang version 3.4:

main.cpp:12:10: error: non-const lvalue reference to type 'MyPOD' cannot bind to a temporary of type 'MyPOD' Agg():m(MyPOD()){} ^ ~~~~~~~ 1 error generated.

Which means you cannot set a non-const reference from a temporary (rvalue).

So, in short, your comment is wrong here:

Agg a;
a.m=p; //referencing the object above

This would be the proper commenting:

Agg a;
a.m=p; //copying the object above by using the assignment operator
László Papp
  • 51,870
  • 39
  • 111
  • 135