Click to See Complete Forum and Search --> : Default Initialization of reference variables in methods


agni256
October 31st, 2002, 01:13 PM
How do I initialize references in my method

BOOL GetSelectedItem(INDEX_DATA& pIndex,
int& nSelItem =-1/*???*/);

Is it possible. I know I am doing wrong, because Compiler tells:cool:

zdf
October 31st, 2002, 04:15 PM
void foo( const int& a_i = -1 )
{
//...
}

I hope it helps.

Regards,

Yves M
October 31st, 2002, 04:34 PM
Well, yes that works, but obviously you won't be able to change a_i, which might defeat the point of passing by reference.

But there is no solution to this (as far as I can imagine), since the reference operator implies that you are actually passing a dereferenced pointer to an int. This means that a_i has to be somewhere in memory as opposed to a constant like -1.

The reason it works with const int& a_i = -1 is that the compiler will allocate a static variable for -1 and &a_i will point to that one. So it's the same as writing :

const static minusone = -1;

void foo(const int &i = minusone)
{
// code
}

CBasicNet
November 1st, 2002, 03:32 AM
Originally posted by Yves M
Well, yes that works, but obviously you won't be able to change a_i, which might defeat the point of passing by reference.

But there is no solution to this (as far as I can imagine), since the reference operator implies that you are actually passing a dereferenced pointer to an int. This means that a_i has to be somewhere in memory as opposed to a constant like -1.

The reason it works with const int& a_i = -1 is that the compiler will allocate a static variable for -1 and &a_i will point to that one. So it's the same as writing :

const static minusone = -1;

void foo(const int &i = minusone)
{
// code
}


The reason for passing reference in such case is to eliminate the need to write a copy constructor (if that needs to be). The const keyword is to tell the user of your function that the object he passed, wouldn't get modifed.

Yves M
November 1st, 2002, 06:05 AM
Yes, I know ;) I was referring to the line of code agni gave :
BOOL GetSelectedItem(INDEX_DATA& pIndex, int& nSelItem =-1);

If he didn't want to modify nSelItem then it would be easier to pass by value rather than try this default initialisation.

Andreas Masur
November 1st, 2002, 07:20 AM
Originally posted by agni256
BOOL GetSelectedItem(INDEX_DATA& pIndex,
int& nSelItem =-1/*???*/);

Besides the problem itself just a note...why do you want to pass a reference to a POD, in this case an integer...

There is no advantage of passing POD's by reference rather than by value. I mean if the above function is just a sample to show the problem it is okay but if this is really what you are looking for than I would just answer...

BOOL GetSelectedItem(INDEX_DATA& pIndex, int nSelItem =-1);

Yves M
November 1st, 2002, 07:36 AM
Well, one use of passing by reference is to allow the function to modify the variable passed (so that the changes are reflected in the caller's scope) and make it clear that that variable is not optional. If you passed by pointer, the caller could always pass NULL and that might not make sense.

For the original question, it does not work either when you are passing a pointer to a variable, unless you specify NULL or make it a pointer to a global variable.

For example, this works:

int minusone;

void func(int *p = &minusone)
{
int i;

i = *p;
}

int main(int argc, char* argv[])
{
minusone = -1;
func();
return 0;
}

But of course this can be a source of endless errors, especially if you modify *p inside func.

jfaust
November 1st, 2002, 08:27 AM
Yes, you can use a global as the default parameter. The problem with that is if the parameter ever changes, you could easily get different behaviors with different, even consecutive, calls to the method. This could bugs that would be difficult to find.

Edit: Which is, umm, what Yves just said.

Jeff

Andreas Masur
November 1st, 2002, 08:43 AM
Originally posted by Yves M
Well, one use of passing by reference is to allow the function to modify the variable passed (so that the changes are reflected in the caller's scope) and make it clear that that variable is not optional.
Well...yes you are right. I think I did not have enough coffee yet... I usually use references only if the function does not change it thus I just const-references...

I think I got so used to it, that I did not think enough...sorry about that... :cool:

Yves M
November 1st, 2002, 08:54 AM
Passing by reference to allow changing the variable is a pretty thorny issue anyways. Here is what Stroustrup says about it (section 5.5 p99).

A reference can be used to specify a function argument so that the function can change the value of an object passed to it. For example:

void increment(int &aa) {aa++;}

void f()
{
int x = 1;
increment(x); // x = 2
}

...
To keep a program readable, it is often best to avoid functions that modify their arguments.

Andreas Masur
November 1st, 2002, 10:11 AM
Originally posted by Yves M
To keep a program readable, it is often best to avoid functions that modify their arguments.
Well...basically that is the reason why I pass pointers while functions modify their arguments and const-references otherwise... :cool:

jfaust
November 1st, 2002, 10:24 AM
Possibly of interest:

Parameters are often passed in as output parameters when the method needs to set more than one value. However, it is possible for methods to return multiple values. The quick answer is to use a struct, but there are better ways in which you don't have to declare a new struct. std::pair works great for returning two values, possibly the value you are interested in as well as an error condition. For more than two values, there is boost::tuple, which can be found at boost (http://www.boost.org).

For arguments that are both input and output, well... ich.

Jeff

Yves M
November 1st, 2002, 10:45 AM
Yeah, well, I do use non-const references for functions with descriptive names, like KeyPlusPlus(KeyType &key) for example. But it's true that it should generally be avoided since it's not clear to the caller that his parameter can be modified and that he can't pass a constant :/

As for multiple return values... Hum... I would still prefer using non-const references, but then I'll document the function well.

jfaust
November 1st, 2002, 10:57 AM
I would still prefer using non-const references, but then I'll document the function well.


I understand. It's a pretty big change from standard practice. However, after adjusting to it, all intentions are clear without extra documentation. I prefer policies that can be enforced by the compiler rather than relying on source documentation.

Jeff

agni256
November 1st, 2002, 01:22 PM
I generally use references if I need to pass back multiple results.
I restrict the return value to BOOL to know whether method was successful.

In the following code:
BOOL DisplayField(int nRow=0, int nCol=0, CString strType="C",
CString strValue="", CString& strDispValue=CString(""),

I could intialize and change the value of stDispValue.
Yves may be correct here that complier needs some memory
to point to.
Can we say then its only primitives that may not work?
Intializing with Objects will, as they have memory to point to..

AnthonyMai
November 1st, 2002, 02:30 PM
To keep a program readable, it is often best to avoid functions that modify their arguments.


Well...basically that is the reason why I pass pointers while functions modify their arguments and const-references otherwise...


Just avoiding functions that modify their arguments is NOT enough to keep a program readable. You have to stay away from using references as function parameters at all, const or not.

References are one of the biggest obstacle in code readability. They are implemented like pointers but they look like objects.
Especially when you pass it in to a function call. Is it pass the object by copying, or is it passed by reference? You never know.

Also keep in mind references do NOT guarantee it actually point to a valid object. It may force you to always initialize when declare reference, but you could initialize it with the wrong thing or even a null pointer.

jflegert
November 1st, 2002, 02:36 PM
Originally posted by AnthonyMai
...
Also keep in mind references do NOT guarantee it actually point to a valid object. It may force you to always initialize when declare reference, but you could initialize it with the wrong thing or even a null pointer.
And the same isn't possible with pointers?

jfaust
November 1st, 2002, 02:43 PM
Just avoiding functions that modify their arguments is NOT enough to keep a program readable. You have to stay away from using references as function parameters at all, const or not.


Bull.


Is it pass the object by copying, or is it passed by reference? You never know.


Of course you know. You look at the fuction declaration. The need to know wether something is passed by value or by const reference is only of interest to optimization.


Also keep in mind references do NOT guarantee it actually point to a valid object. It may force you to always initialize when declare reference, but you could initialize it with the wrong thing or even a null pointer.


This is true for anything passed in, and would require the client to do something invalid. Wether a reference or not, it's not possible for a method to test for these kinds of things. You can safely assume that a passed reference is valid.

There's absolutely nothing wrong with using const references. For non-const references, some people prefer pointers for clarity.

Jeff

AnthonyMai
November 1st, 2002, 02:44 PM
With pointer you can at least easily check to see if the pointer is null, and people commonly do the check. With references it is not easily to do such check, and people NEVER do the check.

Yves M
November 1st, 2002, 02:49 PM
Interesting...

How would you pass a NULL reference with C++ code ?

Just avoiding functions that modify their arguments is NOT enough to keep a program readable. You have to stay away from using references as function parameters at all, const or not.

For passing structs or classes it definitely beats passing as value. I would like to know how you would write an "=" operator for a class then... Hum or is operator overloading also an inherently bad idea ?

jfaust
November 1st, 2002, 02:50 PM
With pointer you can at least easily check to see if the pointer is null, and people commonly do the check. With references it is not easily to do such check, and people NEVER do the check.


To do this, the client would need to dereference a NULL pointer, which results in undefined behavior according to the standard. If the program blows up as a result, it's no fault of the reference parameter.

Jeff

AnthonyMai
November 1st, 2002, 02:53 PM
Is it pass the object by copying, or is it passed by reference? You never know.



Of course you know. You look at the fuction declaration. The need to know wether something is passed by value or by const reference is only of interest to optimization.


Really? Whether something ia passed by value or by reference only matters with optimization? I hope it doesn't take too long for you to realize why that statement is completely wrong.

How is readability when once in every three line you have to open op a different source file to figure out whether the function call is passed by value or passed by reference?

jflegert
November 1st, 2002, 02:53 PM
Originally posted by AnthonyMai
With pointer you can at least easily check to see if the pointer is null, and people commonly do the check. With references it is not easily to do such check, and people NEVER do the check.
See jfaust's reply.

In addition, if you are going to be misusing references, what's so hard about checking for NULL:
void fn(int &n)
{
if (&n == NULL)
return;

...
}

jfaust
November 1st, 2002, 02:58 PM
Really? Whether something ia passed by value or by reference only matters with optimization? I hope it doesn't take too long for you to realize why that statement is completely wrong.


Well, I said by "const reference" and not by "reference", both having very different meanings. I guess I should have said if the item is copy constructable and constructing it has no strange side-effects. Assuming those things, which is a pretty safe assumption, it's an optimization issue.

Jeff

AnthonyMai
November 1st, 2002, 03:14 PM
I would like to know how you would write an "=" operator for a class then... Hum or is operator overloading also an inherently bad idea ?


Operator overloading some times is a VERY BAD idea. It hides something that you should be aware of, which could surprise you if you are not careful.

Look at this simple code line code:


c = a + b;


Simple enough? You would assume a and b would not change. And c becomes what a + b is, depends on what eaxtly a + b is, when the object is none-POD.

WRONG!!!!!!!!

The following code changes BOTH c AND a, although seemingly a and b should not be changed in "c = a + b".


#include <stdio.h>

class A
{
public:
int m_val;
A(int val = 0):m_val(val){};
~A() {}

A& operator = (const A& v2) {m_val = v2.m_val; return *this;}
A& operator + (const A& v2) {m_val += v2.m_val; return *this;}

operator int () {return m_val;}
};

int main(int argc, char* argv[])
{
A a(1),b(2),c(3);

printf("Before: a = %d b = %d c = %d\r\n", a, b, c);

c = a + b;

printf("After: a = %d b = %d c = %d\r\n", a, b, c);

return 0;
}


The output is
Before: a = 1 b = 2 c = 3
After: a = 3 b = 2 c = 3

AnthonyMai
November 1st, 2002, 03:17 PM
I guess I should have said if the item is copy constructable and constructing it has no strange side-effects. Assuming those things, which is a pretty safe assumption, it's an optimization issue.


You do should have said that. And NO those things are NOT a pretty safe assumptions. Calling a construction DO have side effects which are often inportant and can not be ignored.

jfaust
November 1st, 2002, 03:32 PM
The following code changes BOTH c AND a, although seemingly a and b should not be changed in "c = a + b".


This is simply because of a badly written operator+. operator+ should return a const value, as in "const A operator+(const A& v2) const", and should not modify the class. The return value should be const to prevent strangeness like "a+b=c".


Calling a construction DO have side effects which are often inportant and can not be ignored.


Wrong. They CAN have side-effects, but most often don't. I don't consider memory allocation a side-effect. If you are dealing with such a class that has side-effects, you must be careful in everything that you do, including passing those things as parameters. But usually these considerations can be safely ignored.

Jeff

AnthonyMai
November 1st, 2002, 03:53 PM
This is simply because of a badly written operator+. operator+ should return a const value, as in "const A operator+(const A& v2) const", and should not modify the class. The return value should be const to prevent strangeness like "a+b=c".


Totally wrong. Just added "const" to my sample code and the test result is the same.

Please try again to give a valid explaination.

PaulWendt
November 1st, 2002, 03:54 PM
Originally posted by AnthonyMai


#include <stdio.h>

class A
{
public:
// ...
A& operator + (const A& v2) {m_val += v2.m_val; return *this;}
// ...
};




I'm just going to add to what jfaust said and ask WHY you are
assigning to m_val in your operator+(). That's not what
operator+() does. What you wrote was what operator=+()
for your class would have been.

I choose to make operator+ a non-member function for reasons
such as this. Heck, Stroustrup advises this, too, in Section
11.3.1 of The C++ Programming Language.

Edit: formatting

--Paul

PaulWendt
November 1st, 2002, 03:57 PM
Originally posted by AnthonyMai


Totally wrong. Just added "const" to my sample code and the test result is the same.

Please try again to give a valid explaination.

Try something like this:

#include <stdio.h>

class A
{
public:
int m_val;
A(int val = 0):m_val(val){};
~A() {}

A& operator = (const A& v2) {m_val = v2.m_val; return *this;}

operator int () {return m_val;}
};

A operator+(const A& lhs, const A& rhs)
{
return A(lhs.m_val + rhs.m_val);
}

int main(int argc, char* argv[])
{
A a(1),b(2),c(3);

printf("Before: a = %d b = %d c = %d\r\n", a, b, c);

c = a + b;

printf("After: a = %d b = %d c = %d\r\n", a, b, c);

return 0;
}


--Paul

Yves M
November 1st, 2002, 04:05 PM
Well, you must have added the const in the wrong place then...

#include <stdio.h>

class A
{
public:
int m_val;
A(int val = 0):m_val(val){};
~A() {}

A& operator = (const A& v2) {m_val = v2.m_val; return *this;}
// This gives a compiler error
A& operator + (const A& v2) const {m_val += v2.m_val; return *this;}
// This is a way to do it correctly
A operator + (const A& v2) const { return A(m_val + v2.m_val); }

operator int () {return m_val;}
};

int main(int argc, char* argv[])
{
A a(1),b(2),c(3);

printf("Before: a = %d b = %d c = %d\r\n", a, b, c);

c = a + b;

printf("After: a = %d b = %d c = %d\r\n", a, b, c);

return 0;
}

AnthonyMai
November 1st, 2002, 04:07 PM
PaulWendt, you have made some good point. But the point we are talking here is not how to write a correct operator. If any thing, what you pointed out exaxtly enhances my argument.

My argument is not how to correctly write operator overloads using references. My argument is using references (in good way or in bad way) makes code unreadable and could result in things you never expected.

Back to the origin. Look at this code line again:

c = a + b;


Any reasonable reader would read the above code line and make a reasonable assumption that A and B would NOT change. Right?

I have demonstrated, that by using references and operator overloads, (whether that usage is acceptable or not is another iissue.) That you can do some thing some where else and cause the surprising effect of changing a, in "c = a + b", without you even realize it happening. And it could totally escape you attention.

And that is bad.

Yves M
November 1st, 2002, 04:11 PM
Your point is entirely valid since it applies to non-const references. const-references do not change things, so I don't see how they would be bad.

jfaust
November 1st, 2002, 04:22 PM
Totally wrong. Just added "const" to my sample code and the test result is the same.


Anthony,

The point I was making is that the operator+ you provided is incorrect. It's simply flat out wrong. This is a bug, and has nothing to do with readability or references or anything. It's simply wrong.

Jeff

PaulWendt
November 1st, 2002, 04:57 PM
Originally posted by AnthonyMai
PaulWendt, you have made some good point. But the point we are talking here is not how to write a correct operator. If any thing, what you pointed out exaxtly enhances my argument.


I understand your argument, but I have to agree with Yves and
jfaust still. Take into account this code, which takes YOUR
definition of operator+() and takes out all references. Yeah,
only values are used.


#include <stdio.h>

class A
{
public:
int m_val;
A(int val = 0):m_val(val){};
~A() {}

A& operator = (const A& v2) {m_val = v2.m_val; return *this;}
A operator + (A v2) {m_val += v2.m_val; return *this;}

operator int () {return m_val;}
};

int main(int argc, char* argv[])
{
A a(1),b(2),c(3);

printf("Before: a = %d b = %d c = %d\r\n", a, b, c);

c = a + b;

printf("After: a = %d b = %d c = %d\r\n", a, b, c);

return 0;
}


See? The problem doesn't have anything to do with references;
it's that you are using operator+=() inside operator+(). For that
reason, I'm going to have say that it's your implementation of
operator+() that's flawed and not references....

--Paul

Yves M
November 1st, 2002, 05:28 PM
Hehe :) That's true, I hadn't even thought about this ;)

jwbarton
November 4th, 2002, 04:26 PM
While the particular implementation of operator+ using operator+= was not correct, it actually is a good idea to implement operator+ using operator+=. This will link together the operators so that for a more complicated class you don't need to code the same thing multiple times.

From Scott Meyer's book "More Effective C++", Item 22:

template<class T>
const T operator+( const T& lhs, const T&rhs )
{ return T(lhs) += rhs; }


This show a correct implementation of operator+ which uses operator+= to do the work. The actual job of adding 2 items together is then handled by implementing operator+=. Note that in this case a global template was used so that any class which implements operator+= will automatically generate an operator+ which can add 2 items together (if needed).

Best regards,
John

galathaea
November 4th, 2002, 05:45 PM
Originally posted by jfaust
I prefer policies that can be enforced by the compiler rather than relying on source documentation.


I agree with this COMPLETELY. The problem's mentioned so far with passing by reference and operators are problems in the original code usage and should be caught at compile time. This is why I always try to use type-based policies and some of the template paradigms as first elucidated by Barton and Nackman (and expanded upon for the general public in Andrei Alexandrescu's gorgeous Modern C++ Design: Generic Programming and Design Patterns Applied). It is my opinion that proper type-based policies can solve any of these issues which should be caught at compile time. It just seems proper design to me.

AnthonyMai
November 5th, 2002, 10:52 AM
It is my opinion that proper type-based policies can solve any of these issues which should be caught at compile time. It just seems proper design to me.


When the code of exact same syntax and format could be resolved to two different interpretations. What is the use of type checking and how would you suppose the compiler can catch any thing? This happens when references are used, because you never know, when you call a function with a variable, whether a copy of the variable or just the reference of the variable, is passed to the function. If unfortunately you have both a version of the function which takes a copy of the value, and an overloaded one which takes a reference, then even the compiler doesn't know which one you really mean.

The c = a + b example I cited was not intended to demostrate the use of operator, but to demonstrate how this completely innocent looking code could MISLEAD you into thinking a and b would not change but in fact they change. Suppose I unintentionally made a mistake in my operator, it would be really hard to catch what goes wrong.

When I look at a code line, I need to be able to interpret it correctly, without any ambiguity, and without the need to look some where else. that is READABILITY.

If looking at

foo(par1, par2);

I am NOT quite sure that par1, par2 will stay the same (as is the case when par1 is passed in as a reference), then I have a big readability problem.

That is why I am against the use of references, for the sake of complete unambiguitiness and readability. I see no need for references at all.

galathaea
November 5th, 2002, 11:51 AM
That is why I am against the use of references, for the sake of complete unambiguitiness and readability. I see no need for references at all.


I hope this only refers to non-const function parameters (and even then only when one is not looking at optimization). There are many situations (some described above) that require a reference return type, particularly when you desire lvalue status. And const references are preferred to enforce when you do not want block-relative copy semantics.

As for the compile-time type safety of policies, since templates are resolved at compile-time, it is a simple matter to invoke a template policy that will catch any undesired reassignments on any of my data types. Basically, any data I expose to a client can have a policy template typelist attached that will expose only those semantics I wish to allow (even defaults and assignments). In the second given case we are exploring (concerning passing by non-const referrence) I would send the data types that need to be manipulated wrapped up in a smart-pointer with a no-reference no-assignment policy attached. The design of such a policy could follow Stroustrup's point that "An Object may be explicitly converted to a reference type X& if a pointer to that object may be explicitly converted to an X*" although I think that an ambiguous resolver policy might be preferrable (as Mister Mai mentions at the end of his first paragraph).

The basic point is that I should not expose any objects to client programmers that do not have appropriate policies attached that would enforce my design goals. Objects created by the clients that do as described with operator+ would just mean I have bad coders for clients (and as pointed out above, does not rely on the referrence). I too can code badly if desired.

jwbarton
November 5th, 2002, 11:58 AM
To Anthony Mai,

There are specific cases when overloading operators where the use of references is needed. One example is the overloading of operator[]. This operator needs to return something that can be the target of an assignment. You can't return a pointer, because that would require a change to the way that you write the assignment.

For any of the arithmetic operators, while you could pass a copy of the object, this involves constructing and destructing temporary objects. I would avoid this by passing a const reference to the object.

For the type of programming that I am generally working on (CAD systems with various types of points / matrices), I find that overloading operators makes the syntax much easier and more natural. The use of references when overloading these operators improves efficiency and is sometimes necessary.

Best regards,
John

Andreas Masur
November 5th, 2002, 01:03 PM
Originally posted by AnthonyMai
If looking at

foo(par1, par2);

I am NOT quite sure that par1, par2 will stay the same (as is the case when par1 is passed in as a reference), then I have a big readability problem.

As mentioned before, to write the above line you need to take a look at the declaration of 'foo()' otherwise you will not know which and how many parameters etc. need to be passed in. To read the above line you can quickly take a look the declaration as well...

Bottom line is, that you always have the declaration to look at, and except for standard functions you most-likely have to do it if you want to use the function...

Therefore this cannot and is not a valid argument against the use of references...

Additionally with some kind of style guidelines you would even be able to see it while looking at the function call...

foo(refPar1, Par2); // 'refPar1' by reference, 'Par2' by value
foo(pPar1, Par2); // 'pPar1' by pointer, 'Par2' by value

Of course this is only a valid argument while having a style guideline in place and actually confirming to it...