Sign in to follow this  
kelaklub

Ampersand after type

Recommended Posts

kelaklub    160
I saw this function somewhere and have a question about how it has been declared. Can someone please explain the purpose of having the ampersand (&) after the function return type. I thought it meant the function would return a reference, but this function returns the de-referenced (*this) pointer, which I don't think is a reference.
[SOURCE]
// *************************************************************************
// createVector
// RETURN THE VECTOR BEWEEN TWO POINTS.
// *************************************************************************
template < typename tType >
inline const cl_3dVector < tType > & createVector(const cl_3dPoint < tType > & tail, const cl_3dPoint < tType > & head)
{
    this->x = head.x - tail.x;
    this->y = head.y - tail.y;
    this->z = head.z - tail.z;
    return *this;
}
[/SOURCE]

Share this post


Link to post
Share on other sites
SiCrane    11839
The result of dereferencing a pointer is a reference to the object that the pointer points to. So yes, it's returning a reference to the object.

Share this post


Link to post
Share on other sites
kelaklub    160
I sorta get it. So if this function's return type was double and it was only returning say the x component of the vector "return (this->x);", then you would not need the ampersand symbol because the function is returning a type double and not an object. Does that sound right?

Share this post


Link to post
Share on other sites
SiCrane    11839
Returning direct references to a class' internals is rarely a good idea. Either they should just be public variables, so you don't need to have a function that returns a reference, or they should be insulated behind class functions that can maintain their invariants.

Share this post


Link to post
Share on other sites
Zahlman    1682
I find that it helps to think of references, not as part of a variable's type, but as a calling convention - thus, "return a cl_3dVector< tType > by reference" - and then you indeed return such a thing (*this), and the & just causes it to be returned by reference.

The result of returning it by reference is that the same value is used, rather than a copy. That's not so much an optimization (the compiler is pretty good at removing useless copies) as a useful design trick: references can be used as l-values, so for example you could do createVector(myTail, myHead).doSomething();. It is often the case that member operators will return *this by reference, so that you can "chain" them; this is how things like "cout << foo << bar" work - the result of "cout << foo" is cout, with the printing happening as a side effect, so then "cout << bar" gets evaluated (again printing as a side effect and yielding a reference to cout - but this time there is no more to do, so that reference is just thrown on the floor - note that "cout;" is also a valid C++ statement).

Share this post


Link to post
Share on other sites
SiCrane    11839
Quote:
Original post by Zahlman
references can be used as l-values, so for example you could do createVector(myTail, myHead).doSomething();.

You can do that with an rvalue too.

Share this post


Link to post
Share on other sites
Skeleton_V@T    512
You might want to specify const-reference return value. This will ensure no temporary object will be created for efficiency and also protect your member variable from being undesirably mutated.

[Edit:]
inline const cl_3dVector < tType > & createVector(const cl_3dPoint < tType > & tail, const cl_3dPoint < tType > & head)

Well, :-p you've already did it.

Share this post


Link to post
Share on other sites
Zahlman    1682
Quote:
Original post by Skeleton_V@T
You might want to specify const-reference return value. This will ensure no temporary object will be created for efficiency and also protect your member variable from being undesirably mutated.

[Edit:]
inline const cl_3dVector < tType > & createVector(const cl_3dPoint < tType > & tail, const cl_3dPoint < tType > & head)

Well, :-p you've already did it.


Well, that's not quite the whole story...

Share this post


Link to post
Share on other sites
Skeleton_V@T    512
Quote:
Original post by Zahlman
Well, that's not quite the whole story...

Can you explain this a bit further ?
The most relevant question that I've found is
[18.15] Why does the compiler allow me to change an int after I've pointed at it with a const int*?

And he has pointed out one possible situation:

Causing a const int* to point to an int doesn't const-ify the int.
The int can't be changed via the const int*, but if someone else has an int*
(note: no const) that points to ("aliases") the same int, then that int* can be
used to change the int.
For example:


void f(const int* p1, int* p2)
{
int i = *p1; // Get the (original) value of *p1
*p2 = 7; // If p1 == p2, this will also change *p1
int j = *p1; // Get the (possibly new) value of *p1
if (i != j) {
std::cout << "*p1 changed, but it didn't change via pointer p1!\n";
assert(p1 == p2); // This is the only way *p1 could be different
}
}

int main()
{
int x = 5;
f(&x, &x); // This is perfectly legal (and even moral!)
...
}



But in the OP's case, a reference to const value is being returned. Unlike the situation above, I can't have two references - one is const, one isn't - refer to the same thing.

const_cast can't do it.
Reference-to-reference or non-const-pointer-takes-address-of-const-reference doesn't work also.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this