Reference parameters

D

David Fisher

I have been encouraged by someone else to use a reference rather than a
pointer as an "out" parameter to a function, eg.

void doStuff(Thing &out1, Thing &out2)

as opposed to:

void doStuff(Thing *out1, Thing *out2)

The issue I have with this is that when the function is called, you cannot
tell that the value is being modified:

doStuff(thing1, thing2);

This is much less obvious than:

doStuff(&thing1, &thing2);

The only disadvantages I can think of with using pointers here are:

- a NULL pointer could be passed in by mistake
- the body of doStuff() is slightly more verbose (*out1 instead of out1, ->
instead of ".")
- I suppose the pointer could be accidentally reassigned, incremented,
deleted, etc

I would rank readability and clarity in the use of functions as more
important than any of these things (since most of a program's lifetime is
usually maintenance, not necessarily by the original writer). The friend who
was encouraging the use of references said that it was "more C++ish", but
that by itself isn't a very good argument ...

Any comments ?

David F

PS. I am aware of
http://www.parashift.com/c++-faq-lite/references.html#faq-8.6 ...
 
T

Thore Karlsen

I have been encouraged by someone else to use a reference rather than a
pointer as an "out" parameter to a function, eg.

void doStuff(Thing &out1, Thing &out2)

as opposed to:

void doStuff(Thing *out1, Thing *out2)

The issue I have with this is that when the function is called, you cannot
tell that the value is being modified:

doStuff(thing1, thing2);

This is much less obvious than:

doStuff(&thing1, &thing2);

The only disadvantages I can think of with using pointers here are:

- a NULL pointer could be passed in by mistake
- the body of doStuff() is slightly more verbose (*out1 instead of out1, ->
instead of ".")
- I suppose the pointer could be accidentally reassigned, incremented,
deleted, etc

I would rank readability and clarity in the use of functions as more
important than any of these things (since most of a program's lifetime is
usually maintenance, not necessarily by the original writer). The friend who
was encouraging the use of references said that it was "more C++ish", but
that by itself isn't a very good argument ...

Any comments ?

Personally, I prefer to use pointers for out parameters. For everything
else I use const references. The advantage of being able to tell
instantly if something passed to a function could be modified outweighs
the disadvantages I've seen.

I don't buy the argument that pointer arguments are inherently unsafe.
If the caller is always taking the address of existing variables, there
will be no NULL pointers to worry about. On the other hand if the caller
only has pointers at its disposal, you're no better off if you take a
reference. A dereferenced NULL pointer is as bad as a NULL pointer.
Asserting that a pointer argument is non-NULL is natural, and is the
first thing any developer would do in a function that takes pointers. If
you take a reference, you can be passed NULL-references just as easily,
but most developers typically would not check for that.

Sure, it's the caller's responsibility to check the validity of the
arguments passed to the function, but that's true for both cases.
 
?

=?iso-8859-1?Q?Juli=E1n?= Albo

David Fisher escribió:
I have been encouraged by someone else to use a reference rather than a
pointer as an "out" parameter to a function, eg.

void doStuff(Thing &out1, Thing &out2)

as opposed to:

void doStuff(Thing *out1, Thing *out2)

The issue I have with this is that when the function is called, you cannot
tell that the value is being modified:

doStuff(thing1, thing2);

This is much less obvious than:

doStuff(&thing1, &thing2);

Then rename the function:

evaluateThings (thing1, thing2);

Regards.
 
A

Alf P. Steinbach

Personally, I prefer to use pointers for out parameters. For everything
else I use const references.

Check out Andrei Alexandrescu's "Modern C++ Design" for automatic choice
between "by value" and "by const ref".

The advantage of being able to tell
instantly if something passed to a function could be modified outweighs
the disadvantages I've seen.

Unfortunately that advantage requires (1) that no argument contains a
reference or pointer, which restricts the advantage to a very small subset
of functions, (2) familiarity with the specific function or set of functions
that it belongs to, where presumably it's less of a real advantage, to the
degree that is any advantage, (3) for the case of a familiar set of
functions, that they all adhere strictly to the non-ref convention, not a
single unknown case of pass-by-reference, (4) that the function or functions
are sufficiently badly designed that there can be any doubt about their
effects (i.e. they're presumably not your functions), and (5) that you can
always see from the calling code whether the arguments are pointers or not.

With all these restrictions it seems to me to be a very dubious advantage.

There's no need to invoke such an advantage to justify using pointers.


I don't buy the argument that pointer arguments are inherently unsafe.
If the caller is always taking the address of existing variables, there
will be no NULL pointers to worry about. On the other hand if the caller
only has pointers at its disposal, you're no better off if you take a
reference. A dereferenced NULL pointer is as bad as a NULL pointer.
Asserting that a pointer argument is non-NULL is natural, and is the
first thing any developer would do in a function that takes pointers. If
you take a reference, you can be passed NULL-references just as easily,
but most developers typically would not check for that.

Because checking for NULL-pointers in both caller and callee is redundant;
furthermore, because checking that the address of a reference argument
is non-NULL depends on a formally undefined effect and so is at best false
security (which is dangerous); and most of all, because a reference argument
is a contractual obligation on the caller; see below.

Sure, it's the caller's responsibility to check the validity of the
arguments passed to the function, but that's true for both cases.

Check out the Design By Contract (DBC) philosophy, e.g. as described by
Betrand Meyer himself in "Object Oriented Software Construction". The crux
of that philosophy is to clearly place requirements and responsibilities on
the caller (client code) and the callee (routine) so as to both avoid
redundancy and increase clarity. Some C++ programmers use reference arguments
to express that an argument cannot be NULL (which stems from the language
definition, and so is an absolute requirement on the caller), and pointer
arguments to express that those arguments may be NULL (which is just a vague
and context-dependent convention, must be checked if it's not clear).
 
A

Andrew Koenig

The issue I have with this is that when the function is called, you cannot
tell that the value is being modified:

doStuff(thing1, thing2);

This is much less obvious than:

doStuff(&thing1, &thing2);

The only disadvantages I can think of with using pointers here are:

- a NULL pointer could be passed in by mistake
- the body of doStuff() is slightly more verbose (*out1 instead of out1, ->
instead of ".")
- I suppose the pointer could be accidentally reassigned, incremented,
deleted, etc

Would you be willing to have to write

std::cin >> &x;

instead of

std::cin >> x;

?
 
T

Thore Karlsen

Check out Andrei Alexandrescu's "Modern C++ Design" for automatic choice
between "by value" and "by const ref".

"Everything" was too strong, and yes, I've read that and follow it.
Unfortunately that advantage requires (1) that no argument contains a
reference or pointer, which restricts the advantage to a very small subset
of functions, (2) familiarity with the specific function or set of functions
that it belongs to, where presumably it's less of a real advantage, to the
degree that is any advantage, (3) for the case of a familiar set of
functions, that they all adhere strictly to the non-ref convention, not a
single unknown case of pass-by-reference, (4) that the function or functions
are sufficiently badly designed that there can be any doubt about their
effects (i.e. they're presumably not your functions), and (5) that you can
always see from the calling code whether the arguments are pointers or not.

With all these restrictions it seems to me to be a very dubious advantage.

There's no need to invoke such an advantage to justify using pointers.

I don't agree that pointers are inherently bad and their use needs to be
justified.

I prefer readability to purism, real life advantages (for me) to
theoretical disadvantages. In short, it works for me. I've tried the
alternative, and I found it to be less clear.
Because checking for NULL-pointers in both caller and callee is redundant;
furthermore, because checking that the address of a reference argument
is non-NULL depends on a formally undefined effect and so is at best false
security (which is dangerous); and most of all, because a reference argument
is a contractual obligation on the caller; see below.

Yes. And so nobody checks. But that doesn't mean NULL references don't
happen. And when they do happen they are typically harder to catch
because there's no assert to check for them.
Check out the Design By Contract (DBC) philosophy, e.g. as described by
Betrand Meyer himself in "Object Oriented Software Construction". The crux
of that philosophy is to clearly place requirements and responsibilities on
the caller (client code) and the callee (routine) so as to both avoid
redundancy and increase clarity. Some C++ programmers use reference arguments
to express that an argument cannot be NULL (which stems from the language
definition, and so is an absolute requirement on the caller), and pointer
arguments to express that those arguments may be NULL (which is just a vague
and context-dependent convention, must be checked if it's not clear).

That is one convention. Unfortunately the real world does not follow it.
Unless it is explicitly documented that a pointer argument can be NULL,
assume it cannot be.
 
A

Alf P. Steinbach

That is one convention. Unfortunately the real world does not follow it.

<meta>
That response feels uncannily like my first encounters with really bad
low-level management in one of the world's largest consulting firms, where
I worked for some years. It's not a good idea to pick up response-patterns
from bad low-level managers. Instead, try to emulate the good ones, if any.
Below I try to treat the statement as a technical or at least factual/belief
statement. As you can see there is much room also for technical interpretation.
</meta>

In order of my sentences:

A) Do you think DBC is not described by Bertrand Meyer in the mentioned book?

B) Do you think the description of the crux of that philosophy is incorrectly
stated?

C) Do you think it's untrue that some C++ programmers use reference arguments
to express that an argument cannot be NULL?

D) Do you think checking for NULL address of a reference argument is a
well-defined operation?

E) Do you think it's untrue that some C++ programmers use pointers to indicate
that arguments can be NULL?

F) Do you think that convention (E) need not be checked if it's not clear?

G) And/or, do you think that programmers do not use or should better not use
the DBC philosophy?
 
J

Jon Bell

I have been encouraged by someone else to use a reference rather than a
pointer as an "out" parameter to a function, eg.

void doStuff(Thing &out1, Thing &out2)

as opposed to:

void doStuff(Thing *out1, Thing *out2)

[snip pro and con arguments]

In this context, using pointers versus references is pretty much a
religious-type argument. I personally prefer to use references unless I
absolutely need to use pointers, but I'm not going to shoot someone who
prefers otherwise. I'd just as soon not have to maintain their code,
though, just as I expect pointer-believers would just as soon not have to
maintain mine. ;-)
 
D

David Fisher

Andrew Koenig said:
Would you be willing to have to write

std::cin >> &x;

instead of

std::cin >> x;

No, because in this case it is obvious that x is going to be modified ...
Just concerned about readability, that's all.

BTW does anyone have a convention for marking in, out and in/out parameters
? I kind of like the IDL file convention which actually has keywords like
"in" and "out" before parameters, eg. (real example; "boolean" is IDL for
"bool"):

boolean getSATCVessel(in string satcId, out SpecialVessel vessel)

and I have seen a coding standard which requires a comment for each
parameter:

void fred(
int n // in - <description>
int *m // in/out - <description>
);

I guess most argument types are self describing, though (T and const T& are
"in" paremeters, etc) ...

David F
 
J

jeffc

David Fisher said:
I have been encouraged by someone else to use a reference rather than a
pointer as an "out" parameter to a function, eg.

void doStuff(Thing &out1, Thing &out2)

as opposed to:

void doStuff(Thing *out1, Thing *out2)

The issue I have with this is that when the function is called, you cannot
tell that the value is being modified:

doStuff(thing1, thing2);

This is much less obvious than:

doStuff(&thing1, &thing2);

This is a well-known concern, but also one that tends to go away with more
experience. Looking at the function prototype should tell you all you need
to know. A comment before call to doStuff can certainly clarify the purpose
of doStuff, at least part of which is to change the value of the parameters.
The only disadvantages I can think of with using pointers here are:

- a NULL pointer could be passed in by mistake
- the body of doStuff() is slightly more verbose (*out1 instead of out1, ->
instead of ".")
- I suppose the pointer could be accidentally reassigned, incremented,
deleted, etc

I would rank readability and clarity in the use of functions as more
important than any of these things (since most of a program's lifetime is
usually maintenance, not necessarily by the original writer). The friend who
was encouraging the use of references said that it was "more C++ish", but
that by itself isn't a very good argument ...

Rather than calling it more C++ish, I'd say it's actually more readable and
clear, and less error prone than using pointers. Why use all that nasty
pointer syntax inside the doStuff function, when simply using the parameter
name naked is much more clear?
 
J

jeffc

Thore Karlsen said:
I don't buy the argument that pointer arguments are inherently unsafe.
If the caller is always taking the address of existing variables, there
will be no NULL pointers to worry about.

Right. And if the developer never makes any mistakes, there will be no bugs
in the world.
 
J

jeffc

David Fisher said:
BTW does anyone have a convention for marking in, out and in/out parameters
? I kind of like the IDL file convention which actually has keywords like
"in" and "out" before parameters, eg. (real example; "boolean" is IDL for
"bool"):

boolean getSATCVessel(in string satcId, out SpecialVessel vessel)

and I have seen a coding standard which requires a comment for each
parameter:

void fred(
int n // in - <description>
int *m // in/out - <description>
);

That wouldn't solve the OP's concern though, which was in regard to the
calling syntax, not the function definition itself.
 
G

Gary Labowitz

jeffc said:
This is a well-known concern, but also one that tends to go away with more
experience. Looking at the function prototype should tell you all you need
to know. A comment before call to doStuff can certainly clarify the purpose
of doStuff, at least part of which is to change the value of the
parameters.

Well, what you know when looking at a function call passing an argument is
that it might be modified.
If you want guarantees that it won't be, use const.
Yeah, the prototype is your friend here, but it might often be hidden in an
included header. Indeed, what's to stop a coder of a third party library
(where all you know is to call the function) changing an implementation to
reference and neglecting to inform you (through documentation -- you
remember documentation?). The lack of really good documentation system is
one of C++'s worst faults as far as I'm concerned. (Now, Javadocs, there's a
work of art.)

Somebody fill me in: can arrays be passed to a reference or are they always
pointers?
 
J

jeffc

Gary Labowitz said:
Yeah, the prototype is your friend here, but it might often be hidden in an
included header. Indeed, what's to stop a coder of a third party library
(where all you know is to call the function) changing an implementation to
reference and neglecting to inform you (through documentation -- you
remember documentation?).

I don't follow you. What do you mean by "hidden in an included header"?
It's not true that "all you know is to call the function". You must also
have the header, and thus the prototype. Certainly, third party code is
going to be documented, at the very least with the header itself, and it's
essential for any programmer to look at the prototypes before making any
calls.
 
G

Gary Labowitz

jeffc said:
I don't follow you. What do you mean by "hidden in an included header"?

I was suggesting that most people don't bother printing the header when they
include it.
Some of them are fairly difficult to read, in addition to all that. I didn't
mean that the header information wasn't available.
 
D

David Fisher

jeffc said:
Rather than calling it more C++ish, I'd say it's actually more readable and
clear, and less error prone than using pointers. Why use all that nasty
pointer syntax inside the doStuff function, when simply using the parameter
name naked is much more clear?

One way around this would be to use a local reference for readability:

void fred(int *x_ptr)
{
assert(x_ptr != NULL);
int &x = *x_ptr;

... do something with x ...
}

That way the caller can say fred(&x) and the function can still be readable,
too.

I learnt Pascal before C/C++, and it had a useful "with" statement which let
you reference fields of a record (struct) in the next block without needing
to use the full name; using a local reference is a bit like that. I do this
using constant references when I don't want to write something out in full
each time:

const SomeStructType &s = someArrayOfArrays[n].someArrayOfStructs[m];

cout << s.field1 << ", " << s.field2;

David F
 

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

Forum statistics

Threads
474,148
Messages
2,570,838
Members
47,385
Latest member
Joneswilliam01

Latest Threads

Top