References Question (Long)

D

Daniel Wilcox

I have a question, I have been given a piece of code that
apparantly
compiles under Visual C++ but I cannot get to compile under
g++ 3.2.
I have read the FAQ and delved into the Stroustrup book as
well as an
O'Reilly one but please I am not a natural C++ programmer so
allow me
some scope to commit some no-brainers as it were ;)

Now looking at the code here is a simplified example of the
problem I
am trying to resolve:

int f(int &x, int &y)
{
int local;

// do something perverse with x & y store result in local
return(local);
}

int g(int &x, int &y)
{
int local;

// do something depraved with x & y store result in local
return(local);
}

As I understand it passing back local variables in C++ is
considered a
badidea(tm), I'm not sure I fully appreciate the reasoning
behind this
though. Although I guess it's a functional thing and not really
conducive to the idea of objects.

class matrix {
matrix::matrix();
matrix::matrix(int &i, int &j) {
// do something kinky with i & j to create a matrix
}
};

Then the constructor is called something like this,

matrix M(g(a,b) + f(c+d), f(h,t) * g(o,p));

Now this doesn't even compile throwing a "could not convert
g(..) to
int&" error. I understand this, to make it compile I toyed
with the
idea of making g and f return references, as in "int
&g(int&....,"
however the ungoodness of this became immediately apparent
although it
did compile with warnings, creating pointers in C to stack
results
would be considered bad form as well. Another obvious kludge
to get it
to work would be to store the results of the returns in the
calling
function and replace the parameters to the constructor as in:

int tmp_g = g(a,b);
int tmp_f = f(a,b);

matrix M(tmp_g, tmp_f);

This should work but I appreciate that again it is returning
locals,
however I need something working quickly, would this be
reasonable in
the short term? The original code allegedly works under
Windows but
will this be the case with Linux if modified as above, I
understand
this may lead to "undefined" behaviour?

Should I bite the bullet and think about either a) re-write
or b) get
the guy who wrote it to redo it. I am loathe really to do
either as I
havn't really got time to do it or give an impromptu OO
course to the
author.

The questions is why does the original even compile on MSVC
let alone
run? I have so far been unable to get hold of the author of
the code
so I only have his word that it works, but it seems strange.

Thanks for any advice,

Daniel.
 
C

Christian Jaeger

change "int&" to "const int&" in your declarations (for f,g,matrix,...)
(unless you want to modify the arguments in the functions - but then
indeed, a call like matrix(f(..),g(..)) makes no sense).
 
C

Chris Dams

Daniel Wilcox said:
int f(int &x, int &y)
int g(int &x, int &y)
class matrix {
matrix::matrix();
matrix::matrix(int &i, int &j) {
// do something kinky with i & j to create a matrix
}
};
Then the constructor is called something like this,
matrix M(g(a,b) + f(c+d), f(h,t) * g(o,p));
Now this doesn't even compile throwing a "could not convert
g(..) to
int&" error.

You can turn the arguments of matrix::matrix into integers instead of
references to integers. Perhaps you should also do this to your function
f if you want to pass c+d to it.

Bye,
Chris Dams
 
D

Daniel Wilcox

Chris said:
You can turn the arguments of matrix::matrix into integers instead of
references to integers. Perhaps you should also do this to your function
f if you want to pass c+d to it.

Bye,
Chris Dams

That should have been "c,d". Thanks.
 
K

Karl Heinz Buchegger

Daniel said:
Now looking at the code here is a simplified example of the
problem I
am trying to resolve:

int f(int &x, int &y)
{
int local;

// do something perverse with x & y store result in local
return(local);
}

int g(int &x, int &y)
{
int local;

// do something depraved with x & y store result in local
return(local);
}

As I understand it passing back local variables in C++ is
considered a
badidea(tm)

It is. But you are not passing back a local variable.

IN C++ as in C, if you specify nothing special then always a copy
of the value is passed.

Thus in:

void foo( int i );

int main()
{
int a = 7;
foo( a );
}

it is not a that is passed to foo(). It is the current value of a,
which happens to be 7, that is passed to the function.

Some in the other direction:

int foo()
{
int local = 5;
return local;
}

Since the return type of foo is int, a value is passed to the caller.
Thus it is not local that is returned, but it is the current value
of local, which happens to be 5, that is returned.
, I'm not sure I fully appreciate the reasoning
behind this
though.

Because if you return a local variable, you do this by returning
a reference to that local variable. But the local variable is detroyed
at the moment the function exits. Thus the caller is left with a reference
to a variable which no longer exists.
Although I guess it's a functional thing and not really
conducive to the idea of objects.

class matrix {
matrix::matrix();
matrix::matrix(int &i, int &j) {

In case of built in types, like char, int, long, double, pass
per reference is not of much use, if the function does not plan
to modify the callers variable. In your case I would guess that this
is the case, thus you can do:

matrix::matrix( int i, int j ) {
...
// do something kinky with i & j to create a matrix
}
};

Then the constructor is called something like this,

matrix M(g(a,b) + f(c+d), f(h,t) * g(o,p));

g returns an int, so does f. Those 2 ints (numbers) are added
together and sent to the constructor. But your current constructor
says: Hey, I want a reference! You send me some numbers, I want a variable,
cause that is what a reference is: another name for some variable. So give
me a variable"

You could do

int temp = g(a,b) + f(c+d);

matrix M( temp, ....

See: Now I have given the constructor what it demands: a variable, such that
the ctor can take a reference from it.
Now this doesn't even compile throwing a "could not convert
g(..) to
int&" error. I understand this, to make it compile I toyed
with the
idea of making g and f return references, as in "int
&g(int&....,"

Bad idea.
All you need to do, is make the ctor accept plain int instead of
references.
The questions is why does the original even compile on MSVC
let alone
run?

Depends on which version that was and what service packs were installed.
Newer versions of MSVC don't compile it.
 
D

Daniel Wilcox

Karl said:
In case of built in types, like char, int, long, double, pass
per reference is not of much use, if the function does not plan
to modify the callers variable. In your case I would guess that this
is the case, thus you can do:

matrix::matrix( int i, int j ) {
...

I simplified the arguments, there are some instances where
matrix::matrix(int &, int&) is the case. Now I can see how
replacing these as above would work. However there are other
cases where matrix::matrix(matrix &) is used. In these
cases the calls are similar to the examples such that:

matrix f(int &r, int &c, matrix &m)
{
matrix ret;

// do something

return(ret);
}

followed by a call to the constructor such as:


matrix M(f(x,y,m) * f(y,x,m));

So here if I am correct? I could replace this with something
like

matrix a = f(x,y,m);
matrix b = f(y,x,m);
matrix M(a * b);

which makes more sense.

To further confuse me though the code also contains
overloaded operators such as:

matrix operator * (matrix& lhs, matrix& rhs)
{
matrix temp;

// do some multiplication

return (temp);
}
int foo()
{
int local = 5;
return local;
}
Since the return type of foo is int, a value is passed to the caller.
Thus it is not local that is returned, but it is the current value
of local, which happens to be 5, that is returned.

I understand the above example. In the case of the operator
example when returning temp am I correct to say that this
will not be the return of a value? Where returning local=5
is correct returning an object is in fact the idea that is a
bad one? Would I be correct in saying that the author seems
to have got a bit confused with C and C++ returning
conventions, treating returns of objects as returns of
values as in C?

I know its confusing me! ;)

Thanks, Daniel.
 
J

jeffc

Daniel Wilcox said:
// do something perverse with x & y store result in local
return(local);
}

int g(int &x, int &y)
{
int local;

// do something depraved with x & y store result in local
return(local);
}

As I understand it passing back local variables in C++ is
considered a
badidea(tm), I'm not sure I fully appreciate the reasoning
behind this
though.

No, that's not necessarily true. You're returning the *value* of the local
int, but the actual storage used is going to be a temporary int that does
not depend on "local" staying around permanently. If you had returned a
*pointer* to local, then that would be bad, because the storage for local is
gone after the function exits.

Before we go any further, why are the parameters x and y declared as
references?
 
J

jeffc

Christian Jaeger said:
change "int&" to "const int&" in your declarations (for f,g,matrix,...)

But that really makes no sense either. It should just be "int" in that
case.
 
D

Daniel Wilcox

jeffc said:
No, that's not necessarily true. You're returning the *value* of the local
int, but the actual storage used is going to be a temporary int that does
not depend on "local" staying around permanently. If you had returned a
*pointer* to local, then that would be bad, because the storage for local is
gone after the function exits.

Before we go any further, why are the parameters x and y declared as
references?

In the actual code they are not int's but objects, I
realise, now, that I have not exactly been clear about this.
Yes I understand the passing of variables, but I think I
have confused myself with the passing of objects and how
this differs. Im going for a coffee.
 
K

Karl Heinz Buchegger

I simplified the arguments, there are some instances where
matrix::matrix(int &, int&) is the case. Now I can see how
replacing these as above would work. However there are other
cases where matrix::matrix(matrix &) is used. In these
cases the calls are similar to the examples such that:

matrix f(int &r, int &c, matrix &m)
{
matrix ret;

// do something

return(ret);
}

I suppose the passed matrix m, is not changed. In such cases,
you do it, by passing a reference, but it should be a const reference:

matrix f( int r, int c, const matrix& m )
{

....
followed by a call to the constructor such as:

matrix M(f(x,y,m) * f(y,x,m));

So here if I am correct? I could replace this with something
like

matrix a = f(x,y,m);
matrix b = f(y,x,m);
matrix M(a * b);

If you make the above change you could leave it at:

matrix M( f(x,y,m) * f(y,x,m) );


updated rules of thumb:
* builtin types (int, long, double, etc...)

you want the function
* to not be able to alter the callers variable ->
pass by value

voif foo( int i );

* be able to alter the callers variable ->
pass by reference

void foo( int & i );

* user defined class types (eg. your matrix class)

you want the function
* to not be able to alter the callers variable ->
pass by const reference

voif foo( const matrix & m );

* be able to alter the callers variable ->
pass by reference

void foo( matrix & m );


if you get a value from a function as a return value, then you
can feed this value into that function only, if:
* the function takes the argument with call by value
* the function takes the argument by const reference

What you get from the function is a temporary. And you can't bind
a temporary to a non const reference.

which makes more sense.

To further confuse me though the code also contains
overloaded operators such as:

matrix operator * (matrix& lhs, matrix& rhs)
{
matrix temp;

// do some multiplication

return (temp);
}

Here you *must* return a value and not a reference. After
this function returns, temp no longer exists, so accessing
it from outside through a returned reference would be a severe
error.
I understand the above example. In the case of the operator
example when returning temp am I correct to say that this
will not be the return of a value?

Other way round. It returns the value. Only the value.
Where returning local=5
is correct returning an object is in fact the idea that is a
bad one? Would I be correct in saying that the author seems
to have got a bit confused with C and C++ returning
conventions, treating returns of objects as returns of
values as in C?

It's very simple: If you have a plain and simple data type
as your return type, then you return by value: a copy of
the variable (or the result of an expression) is made and that
copied value is sent back.

int foo()
{
return 5; // return value 5
}

int foo()
{
int i = 8;
return i; // look up i, and return the value you
// found in i
}

matrix foo()
{
matrix m;
return m; // look up m and return the value you found
// in m.
}

In all cases: After the function has returned, whatever was inside
that function (eg. local variables) are no longer needed, since
a copy of the return value has already been made and returned to
the caller.
 
J

jeffc

Daniel Wilcox said:
In the actual code they are not int's but objects, I
realise, now, that I have not exactly been clear about this.
Yes I understand the passing of variables, but I think I
have confused myself with the passing of objects and how
this differs. Im going for a coffee.

Well, it doesn't really differ. It's just that you would only pass by
reference if you intended to change the value of those arguments in the
function. The problem here lies with your misunderstaning of what "return"
does with the return value of the function. Let's take a simpler example
because I think the bigger ones are confusing you. There's a fundamental
problem here. For example, do you understand this?

int f()
{
return 1;
}

int main()
{
int i;
i = f();
}
 
K

Karl Heinz Buchegger

Karl said:
if you get a value from a function as a return value, then you
can feed this value into that function only, if:
* the function takes the argument with call by value
* the function takes the argument by const reference

that means:

if - call by value - OR - call by const reference
Either of the 2 possibilities would be fine.
What you get from the function is a temporary. And you can't bind
a temporary to a non const reference.

That's why the compiler barfs at your original example:
The function took the matrix per reference. But a temporary
cannot be bound to a reference. It has to be a const reference.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top