473,387 Members | 3,810 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,387 software developers and data experts.

Pass array by value

Hi,

I am just learning about the array/pointer duality in C/C++. I couldn't
help wondering, is there a way to pass an array by value? It seems like
the only way to do is to pass it by reference??

Thanks,
BB
Jul 23 '05 #1
41 8254
Berk Birand wrote:
Hi,

I am just learning about the array/pointer duality in C/C++. I couldn't
help wondering, is there a way to pass an array by value? It seems like
the only way to do is to pass it by reference??

Thanks,
BB

When an array is passed in either C or C++ (different, oft subtly
incompatible languages with a common subset), it decays into a pointer
to its first element; the pointer is passed by value.

The only way to simulate passing an array by value would be to wrap it
in a struct.

HTH,
--ag

--
Artie Gold -- Austin, Texas
http://it-matters.blogspot.com (new post 12/5)
http://www.cafepress.com/goldsays
Jul 23 '05 #2
Do not use an array - use std::vector instead.
"Berk Birand" <gr******@yahoo.com> skrev i en meddelelse
news:1107492506.5f08d4c39f894327dfa88bc7c283a9ce@t eranews...
Hi,

I am just learning about the array/pointer duality in C/C++. I couldn't
help wondering, is there a way to pass an array by value? It seems like
the only way to do is to pass it by reference?? Also do not use pointers if you can avoid them.
Thanks,
BB

Jul 23 '05 #3
Peter Koch Larsen wrote:
Do not use an array - use std::vector instead.
"Berk Birand" <gr******@yahoo.com> skrev i en meddelelse
news:1107492506.5f08d4c39f894327dfa88bc7c283a9ce@t eranews...
Hi,

I am just learning about the array/pointer duality in C/C++. I couldn't
help wondering, is there a way to pass an array by value? It seems like
the only way to do is to pass it by reference??


Also do not use pointers if you can avoid them.
Thanks,
BB



Thanks to both for your answers. Also another question that pops in my
mind is whether to use references or pointers in function headers. Is it
better practice to declare a function as, say:

void afunc(int& i);

or is it better to declare it as

void afunc(int* i);

I know that in the second case, one has to explicitly dereference i
whenever one wants to access it. But this can be considered a good
thing, as it makes it more obvious that you are manipulating pointers,
and the values are going to change. On the other hand, it's just more work!

So, what do you say about this?
Jul 23 '05 #4
Berk Birand wrote:
I am just learning about the array/pointer duality in C/C++.
I couldn't help wondering,
"Is there a way to pass an array by value?"
It seems like the only way to do is to pass it by reference?


Actually, you pass a pointer to the first element of the array.

The only way to pass an array by value is to encapsulate it in a struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}

Jul 23 '05 #5
Berk Birand wrote:
Also another question that pops in my mind mind
is whether to use references or pointers in function headers.
Is it better practice to declare a function as, say:

void afunc(int& i);

Or is it better to declare it as

void afunc(int* i);

I know that, in the second case,
one has to explicitly dereference i whenever one wants to access it.
But this can be considered a good thing,
as it makes it more obvious that
you are manipulating pointers, and the values are going to change.
On the other hand, it's just more work!


Avoid functions that modify their arguments.
Pass a const reference and return a result by value. Write

int afunct(const int& i);

or

int afunct(const int* p);

instead. I pass small objects by value

int afunct(int i);

but you can easily substitute a function declaration/definition
that passes a const reference

in afunct(const int& i);

without breaking any applications that call afunct.
You need only recompile and relink.
Jul 23 '05 #6
The only way to pass an array by value is to encapsulate it in a struct:
#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}


this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!

Jul 23 '05 #7

puzzlecracker wrote:
The only way to pass an array by value is to encapsulate it in a

struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}


this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!

#include <iostream>

struct Array {
int A[10];
Arrray(const Array &a)
{
if(a)
while(*A++=*a++);

else
A=0;

}

};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout<<std::endl;

Jul 23 '05 #8
puzzlecracker wrote:
The only way to pass an array by value is to encapsulate it in a
struct:
#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}

this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. Please correct me if I am wrong!


You are wrong.

You can verify this by writing a simple test program:
cat main.cpp

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}

int main(int argc, char* argv[]) {
Array a;
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
A[j] = j;
f(a);
return 0;
}

then compiling and running it.
Jul 23 '05 #9
"Berk Birand" <gr******@yahoo.com> wrote in message
Thanks to both for your answers. Also another question that pops in my
mind is whether to use references or pointers in function headers. Is it
better practice to declare a function as, say:

void afunc(int& i);

or is it better to declare it as

void afunc(int* i);
If one is allowed to pass in a NULL integer (not zero, which is something),
meaning that 'i' is not supplied, then you have to choose the second
version. Otherwise prefer the first method as it makes it clear that the
caller has to pass in a valid integer.
I know that in the second case, one has to explicitly dereference i
whenever one wants to access it. But this can be considered a good
thing, as it makes it more obvious that you are manipulating pointers,
and the values are going to change. On the other hand, it's just more

work!

It's more work to type, not to the end program usually. Most compilers
implement references as pointers, except the pointer notation is not visible
to the end user. So both ways are on an equal footing, and which way is
better is your choice.
Jul 23 '05 #10

"puzzlecracker" <ir*********@gmail.com> wrote in message
news:11**********************@l41g2000cwc.googlegr oups.com...
The only way to pass an array by value is to encapsulate it in a

struct:

#include <iostream>

struct Array {
int A[10];
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout << std::endl;
}


this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!


You are wrong in this case, because the array is statically initialized.
You would be correct if it were dynamically initialized, as in the following
example:

#include <iostream>
#include <iterator>
#include <algorithm>

class Array {
int* p_;
size_t sz_;
public:
typedef int* iterator;
typedef const int* const_iterator;

iterator begin() {return p_;}
iterator end() {return p_ + sz_;}
const_iterator begin() const {return p_;}
const_iterator end() const {return p_ + sz_;}

Array(size_t sz) : p_(new int[sz]), sz_(sz) {}
Array(Array const& a) : p_(new int[a.sz_]), sz_(a.sz_) {
std::copy(a.begin(), a.end(), begin());
}
~Array() {
if (p_) {
delete [] p_;
p_=0; sz_=0;
}
}

};

void print(Array a) { // pass by value
using std::copy;
using std::cout;

copy(a.begin(), a.end(), std::ostream_iterator<int>(cout," "));
cout << std::endl;
}

int main() {
Array a(10);
Array::iterator in=a.begin();

for (int i=0; in!=a.end(); ++in,++i)
*in=i;

print(a);
}
Jul 23 '05 #11
In message <11**********************@c13g2000cwb.googlegroups .com>,
puzzlecracker <ir*********@gmail.com> writes

#include <iostream>

struct Array {
int A[10];
Arrray(const Array &a)
{
if(a)
while(*A++=*a++);
else
A=0;
}
};

void f(Array a) {
int* A = a.A;
for (size_t j = 0; j < 10; ++j)
std::cout << ' ' << A[j];
std::cout<<std::endl;


What is that supposed to do?
Have you tried compiling it?

--
Richard Herring
Jul 23 '05 #12
In message <ct**********@nntp1.jpl.nasa.gov>, E. Robert Tisdale
<E.**************@jpl.nasa.gov> writes
Berk Birand wrote:
Also another question that pops in my mind mind
is whether to use references or pointers in function headers.
Is it better practice to declare a function as, say:
void afunc(int& i);
Or is it better to declare it as
void afunc(int* i);
I know that, in the second case,
one has to explicitly dereference i whenever one wants to access it.
But this can be considered a good thing,
as it makes it more obvious that
you are manipulating pointers, and the values are going to change.
On the other hand, it's just more work!


Avoid functions that modify their arguments.
Pass a const reference and return a result by value. Write

int afunct(const int& i);

or

int afunct(const int* p);

instead.


Would you give the same advice if the object being modified were a
million-element vector?

--
Richard Herring
Jul 23 '05 #13

Berk Birand wrote:
Thanks to both for your answers. Also another question that pops in my mind is whether to use references or pointers in function headers. Is it better practice to declare a function as, say:

void afunc(int& i);

or is it better to declare it as

void afunc(int* i);

I know that in the second case, one has to explicitly dereference i
whenever one wants to access it. But this can be considered a good
thing, as it makes it more obvious that you are manipulating pointers, and the values are going to change. On the other hand, it's just more work!
So, what do you say about this?


This is a question of personal preferences. I prefer to use a pointer,
because when you use the function, it is more explicit that you are
modifying the value:

SomeStruct s = { ... };
modifyingFunc(&s); // might be modified.

However, if the function does not modify, then I prefer to use const
ref for big structs/classes and just copy by value for small ones:

SomeStruct s = { ... };
nonModifyingFunc(s); // will not be modified

Again, this is just personal preference (it is not a *rule*), but I
feel it makes code easier to read especially when you don't want to
keep looking up function definitions.

Hope this helps,
-shez-

Jul 23 '05 #14
In message <36*************@individual.net>, Dave Moore
<dt*****@email.unc.edu> writes

"puzzlecracker" <ir*********@gmail.com> wrote in message
news:11**********************@l41g2000cwc.googleg roups.com...
> The only way to pass an array by value is to encapsulate it in a struct:
>
> #include <iostream>
>
> struct Array {
> int A[10];
> };
>
> void f(Array a) {
> int* A = a.A;
> for (size_t j = 0; j < 10; ++j)
> std::cout << ' ' << A[j];
> std::cout << std::endl;
> }


this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!


You are wrong in this case, because the array is statically initialized.
You would be correct if it were dynamically initialized, as in the following
example:


The issue here isn't between "static" versus "dynamic" initialisation.
Those terms have a different meaning. It's the fact that in your example
the class member is a *pointer* to the data. Pointers tend to invoke the
Rule of Three.
#include <iostream>
#include <iterator>
#include <algorithm>

class Array {
int* p_;
size_t sz_;
public:
typedef int* iterator;
typedef const int* const_iterator;

iterator begin() {return p_;}
iterator end() {return p_ + sz_;}
const_iterator begin() const {return p_;}
const_iterator end() const {return p_ + sz_;}

Array(size_t sz) : p_(new int[sz]), sz_(sz) {}
Array(Array const& a) : p_(new int[a.sz_]), sz_(a.sz_) {
std::copy(a.begin(), a.end(), begin());
}
~Array() {
if (p_) { Superfluous test delete [] p_;
p_=0; sz_=0; Why zero something that's about to be destroyed anyway? }
}
You're living dangerously. Where's the copy assignment operator? The
compiler-generated one isn't going to work, for the same reason that you
had to provide a copy constructor and a destructor.
};

void print(Array a) { // pass by value
using std::copy;
using std::cout;

copy(a.begin(), a.end(), std::ostream_iterator<int>(cout," "));
cout << std::endl;
}

int main() {
Array a(10);
Array::iterator in=a.begin();

for (int i=0; in!=a.end(); ++in,++i)
*in=i;

print(a);
}


--
Richard Herring
Jul 23 '05 #15
Richard Herring wrote:

In message <36*************@individual.net>, Dave Moore
<dt*****@email.unc.edu> writes

"puzzlecracker" <ir*********@gmail.com> wrote in message
news:11**********************@l41g2000cwc.googleg roups.com...

> The only way to pass an array by value is to encapsulate it in a
struct:
>
> #include <iostream>
>
> struct Array {
> int A[10];
> };
>
> void f(Array a) {
> int* A = a.A;
> for (size_t j = 0; j < 10; ++j)
> std::cout << ' ' << A[j];
> std::cout << std::endl;
> }

this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!


You are wrong in this case, because the array is statically initialized.
You would be correct if it were dynamically initialized, as in the following
example:


The issue here isn't between "static" versus "dynamic" initialisation.
Those terms have a different meaning. It's the fact that in your example
the class member is a *pointer* to the data.


How come?
In the posted example indeed an array is a member of the struct and not
a pointer. No further action is required. When the struct is passed per
value, a copy of the entire struct is generated and thus the array itself
gets copied.

So, Dave Moore is right
puzzlecracker is wrong
Tisdale is right (who posted that example)
--
Karl Heinz Buchegger
kb******@gascad.at
Jul 23 '05 #16
In message <42***************@gascad.at>, Karl Heinz Buchegger
<kb******@gascad.at> writes
Richard Herring wrote:

In message <36*************@individual.net>, Dave Moore
<dt*****@email.unc.edu> writes
>
>"puzzlecracker" <ir*********@gmail.com> wrote in message
>news:11**********************@l41g2000cwc.googleg roups.com...
>>
>> > The only way to pass an array by value is to encapsulate it in a
>> struct:
>> >
>> > #include <iostream>
>> >
>> > struct Array {
>> > int A[10];
>> > };
>> >
>> > void f(Array a) {
>> > int* A = a.A;
>> > for (size_t j = 0; j < 10; ++j)
>> > std::cout << ' ' << A[j];
>> > std::cout << std::endl;
>> > }
>>
>> this is not going to work - you need to define a copy constructor,
>> otherwise you still passing an address fo the first element i.e. not
>> copied first element. please correct me if I am wrong!
>>
>
>You are wrong in this case, because the array is statically initialized.
>You would be correct if it were dynamically initialized, as in the following
>example:
The issue here isn't between "static" versus "dynamic" initialisation.
Those terms have a different meaning. It's the fact that in your example
the class member is a *pointer* to the data.


How come?
In the posted example indeed an array is a member of the struct and not
a pointer. No further action is required. When the struct is passed per
value, a copy of the entire struct is generated and thus the array itself
gets copied.


Yes, that's quite true. Note that you didn't use the words "static",
"dynamic" or "initialization". The point is that the compiler-generated
shallow copy will suffice for an array, but you have to write your own
deep copy for what a pointer points to.

So, Dave Moore is right
His example is (mostly) right. His explanation is wrong.
puzzlecracker is wrong
Tisdale is right (who posted that example)


--
Richard Herring
Jul 23 '05 #17

"Richard Herring" <ju**@[127.0.0.1]> wrote in message
news:Ws**************@baesystems.com...
In message <36*************@individual.net>, Dave Moore
<dt*****@email.unc.edu> writes

"puzzlecracker" <ir*********@gmail.com> wrote in message
news:11**********************@l41g2000cwc.googleg roups.com...

> The only way to pass an array by value is to encapsulate it in a
struct:
>
> #include <iostream>
>
> struct Array {
> int A[10];
> };
>
> void f(Array a) {
> int* A = a.A;
> for (size_t j = 0; j < 10; ++j)
> std::cout << ' ' << A[j];
> std::cout << std::endl;
> }

this is not going to work - you need to define a copy constructor,
otherwise you still passing an address fo the first element i.e. not
copied first element. please correct me if I am wrong!


You are wrong in this case, because the array is statically initialized.
You would be correct if it were dynamically initialized, as in the followingexample:


The issue here isn't between "static" versus "dynamic" initialisation.
Those terms have a different meaning. It's the fact that in your example
the class member is a *pointer* to the data. Pointers tend to invoke the
Rule of Three.


Well, perhaps my explanation would have been more clear if I had said static
and dynamic *allocation* instead of initialization. However, you can't
initialize something unless you have allocated storage for it, so my given
explanation is correct by implication.

#include <iostream>
#include <iterator>
#include <algorithm>

class Array {
int* p_;
size_t sz_;
public:
typedef int* iterator;
typedef const int* const_iterator;

iterator begin() {return p_;}
iterator end() {return p_ + sz_;}
const_iterator begin() const {return p_;}
const_iterator end() const {return p_ + sz_;}

Array(size_t sz) : p_(new int[sz]), sz_(sz) {}
Array(Array const& a) : p_(new int[a.sz_]), sz_(a.sz_) {
std::copy(a.begin(), a.end(), begin());
}
~Array() {
if (p_) {

Superfluous test
delete [] p_;
p_=0; sz_=0;

Why zero something that's about to be destroyed anyway?


These are habits I picked up before the standardization of C++, when
compilers didn't always handle destruction of base classes properly. I
spent a long time trying to figure out why the ^&(*^ I was getting
seg-faults during the destruction phase after main had finished. Putting
such superfluous checks and zero-sets seemed got rid of the errors, for
whatever reason. You are of course correct that they are probably
unnecessary with current compilers. However I haven't had to write a
container class like the above for a while .. I use STL containers now ..
much easier 8*).
}
}


You're living dangerously. Where's the copy assignment operator? The
compiler-generated one isn't going to work, for the same reason that you
had to provide a copy constructor and a destructor.


Point taken .. omitting the copy assignment operator was an oversight ... I
was just trying to come up with a quick illustration to help alleviate
puzzlecracker's confusion.

[snip]

Dave Moore
Jul 23 '05 #18
On Thu, 03 Feb 2005 23:48:25 -0500 in comp.lang.c++, Berk Birand
<gr******@yahoo.com> wrote,
I am just learning about the array/pointer duality in C/C++. I couldn't
help wondering, is there a way to pass an array by value?
Not directly, no.
It seems like the only way to do is to pass it by reference??


The assumption in C was that it would be a horrible thing to copy
arrays in order to call functions. Too big, too expensive.

Of course in C++ you will typically be using std::vector instead of
bare naked arrays for ordinary application purposes. So, the task
becomes that of being careful to pass by reference, and not by value
requiring a copy.

Jul 23 '05 #19
In message <36*************@individual.net>, Dave Moore
<dt*****@email.unc.edu> writes

"Richard Herring" <ju**@[127.0.0.1]> wrote in message
news:Ws**************@baesystems.com...
In message <36*************@individual.net>, Dave Moore
<dt*****@email.unc.edu> writes
>
>"puzzlecracker" <ir*********@gmail.com> wrote in message
>news:11**********************@l41g2000cwc.googleg roups.com...
>>
>> > The only way to pass an array by value is to encapsulate it in a
>> struct:
>> >
>> > #include <iostream>
>> >
>> > struct Array {
>> > int A[10];
>> > };
>> >
>> > void f(Array a) {
>> > int* A = a.A;
>> > for (size_t j = 0; j < 10; ++j)
>> > std::cout << ' ' << A[j];
>> > std::cout << std::endl;
>> > }
>>
>> this is not going to work - you need to define a copy constructor,
>> otherwise you still passing an address fo the first element i.e. not
>> copied first element. please correct me if I am wrong!
>>
>
>You are wrong in this case, because the array is statically initialized.
>You would be correct if it were dynamically initialized, as in thefollowing >example:


The issue here isn't between "static" versus "dynamic" initialisation.
Those terms have a different meaning. It's the fact that in your example
the class member is a *pointer* to the data. Pointers tend to invoke the
Rule of Three.


Well, perhaps my explanation would have been more clear if I had said static
and dynamic *allocation* instead of initialization. However, you can't
initialize something unless you have allocated storage for it, so my given
explanation is correct by implication.


Not really. What I'm objecting to is the words "static" and "dynamic",
whatever it is that they describe. Not all use of pointers implies
dynamic allocation. Objects in automatic storage aren't static.

The real issue is the difference between shallow and deep copying, and
the circumstances (ownership of a resource that lies outside the object)
that require the latter.

[snip]
--
Richard Herring
Jul 23 '05 #20
Richard Herring wrote:
In message <ct**********@nntp1.jpl.nasa.gov>, E. Robert Tisdale
<E.**************@jpl.nasa.gov> writes
Berk Birand wrote:
Also another question that pops in my mind mind
is whether to use references or pointers in function headers.
Is it better practice to declare a function as, say:
void afunc(int& i);
Or is it better to declare it as
void afunc(int* i);
I know that, in the second case,
one has to explicitly dereference i whenever one wants to access it.
But this can be considered a good thing,
as it makes it more obvious that
you are manipulating pointers, and the values are going to change.
On the other hand, it's just more work!

Avoid functions that modify their arguments.
Pass a const reference and return a result by value. Write

int afunct(const int& i);

or

int afunct(const int* p);

instead.

Would you give the same advice
if the object being modified were a million-element vector?


Yes.

Elaborate a little. An example might help.
You might be surprised
Jul 23 '05 #21
Siemel Naran wrote:
Berk Birand wrote:
Thanks to both for your answers. Also another question that pops in my
mind is whether to use references or pointers in function headers.
Is it better practice to declare a function as, say:

void afunc(int& i);

or is it better to declare it as

void afunc(int* i);

If one is allowed to pass in a NULL integer (not zero, which is something),
meaning that 'i' is not supplied, then you have to choose the second
version. Otherwise prefer the first method as it makes it clear that the
caller has to pass in a valid integer.


A better option would be to overload the function

void afunc(void);

"to pass in a NULL integer".
Jul 23 '05 #22
"Berk Birand" <gr******@yahoo.com> wrote in message
news:1107494123.e654f06c02003deecd6e37652848b1df@t eranews...
Peter Koch Larsen wrote:
Do not use an array - use std::vector instead.
"Berk Birand" <gr******@yahoo.com> skrev i en meddelelse
news:1107492506.5f08d4c39f894327dfa88bc7c283a9ce@t eranews...
Hi,

I am just learning about the array/pointer duality in C/C++. I couldn't
help wondering, is there a way to pass an array by value? It seems like
the only way to do is to pass it by reference??


Also do not use pointers if you can avoid them.
Thanks,
BB



Thanks to both for your answers. Also another question that pops in my
mind is whether to use references or pointers in function headers. Is it
better practice to declare a function as, say:

void afunc(int& i);

or is it better to declare it as

void afunc(int* i);

I know that in the second case, one has to explicitly dereference i
whenever one wants to access it. But this can be considered a good thing,
as it makes it more obvious that you are manipulating pointers, and the
values are going to change. On the other hand, it's just more work!

So, what do you say about this?


Consider these possible calling sequences with a comment saying what they
look like to me:

int j = 0, k;
BigObject obj;

func1(j); // inputting an int
k = func2(j); // inputting an int and outputting another int
func3(&obj); // outputting an object
func4(obj); // inputting an object by const reference
func5(&j, &k); // outputting two ints

Of course many of these reasonable guesses could be wrong depending on the
actual declarations, but I think the code is easier to understand if you
write your declarations in such a way that they are right! So that means:

1) express output by return value if possible
2) use a pointer to express output otherwise
3) express input by call-by-value or const reference
4) never use a non-const reference (looks like input)

Note that using this system

func6(&j); // should have been either j = func6(); or j = func6(j);

I break these "rules" myself whenever I feel like it, so don't take this too
seriously. On the other hand, when you have a choice it is a good idea to
give some thought to what the calling sequence will look like.

One further note: using exceptions for error handling lets you make some of
these distinctions. If every function returns an error code it muddies up
the waters.

Finally: the distinction between output by pointer and update by pointer
cannot be made by syntax alone.

--
Cy
http://home.rochester.rr.com/cyhome/
Jul 23 '05 #23
Cy Edmunds wrote:
int j = 0, k;
BigObject obj;

func1(j); // inputting an int
k = func2(j); // inputting an int and outputting another int
func3(&obj); // outputting an object
func4(obj); // inputting an object by const reference
func5(&j, &k); // outputting two ints

Of course many of these reasonable guesses could be wrong depending
on the actual declarations, but I think the code is easier to
understand if you write your declarations in such a way that they are
right! So that means:

1) express output by return value if possible
2) use a pointer to express output otherwise
3) express input by call-by-value or const reference
4) never use a non-const reference (looks like input)


4) seems questionable. There's a broad class of cases where you have an object
that's noncopyable or expensive to copy -- such as a stream or a container --
which you want to pass to a modifying algorithm.

Generally, if you believe that functions which operate on a objects of a given
type should be non-members as often as possible, then most of the functions
which would otherwise be non-const member functions will become non-member
functions taking a reference to non-const.

Jonathan

Jul 23 '05 #24
"E. Robert Tisdale" <E.**************@jpl.nasa.gov> wrote in message
news:cu0hni$2t0
Siemel Naran wrote:

If one is allowed to pass in a NULL integer (not zero, which is something), meaning that 'i' is not supplied, then you have to choose the second
version. Otherwise prefer the first method as it makes it clear that the caller has to pass in a valid integer.


A better option would be to overload the function

void afunc(void);

"to pass in a NULL integer".


Say you call a function find(x,y) that returns a pointer to an integer, and
NULL if the integer does not exist. Then one could just say

afunc(find(x,y));

where the signature of afunc is void afunc(int *). Surely this is more
convenient than

int * f = find(x,y);
if (f) afunc(*f);
else afunc();
Jul 23 '05 #25
"Jonathan Turkanis" <te******@kangaroologic.com> wrote in message
news:36*************@individual.net...
Cy Edmunds wrote:
int j = 0, k;
BigObject obj;

func1(j); // inputting an int
k = func2(j); // inputting an int and outputting another int
func3(&obj); // outputting an object
func4(obj); // inputting an object by const reference
func5(&j, &k); // outputting two ints

Of course many of these reasonable guesses could be wrong depending
on the actual declarations, but I think the code is easier to
understand if you write your declarations in such a way that they are
right! So that means:

1) express output by return value if possible
2) use a pointer to express output otherwise
3) express input by call-by-value or const reference
4) never use a non-const reference (looks like input)
4) seems questionable. There's a broad class of cases where you have an
object
that's noncopyable or expensive to copy -- such as a stream or a
container --
which you want to pass to a modifying algorithm.


This rule set suggests using a pointer in this case. I mentioned that
updating and output by pointers would be indistinguishable.

Generally, if you believe that functions which operate on a objects of a
given
type should be non-members as often as possible,
(I certainly do agree.)
then most of the functions
which would otherwise be non-const member functions will become non-member
functions taking a reference to non-const.

Jonathan


There is no reason to make functions into member functions to avoid
non-const references. Use pointers:

func3(&obj); // outputting an object

Again, outputting and updating are not distinguishable.

--
Cy
http://home.rochester.rr.com/cyhome/
Jul 23 '05 #26
Cy Edmunds wrote:
"Jonathan Turkanis" <te******@kangaroologic.com> wrote in message
news:36*************@individual.net...
Cy Edmunds wrote:
4) never use a non-const reference (looks like input)
4) seems questionable. There's a broad class of cases where you have
an object
that's noncopyable or expensive to copy -- such as a stream or a
container --
which you want to pass to a modifying algorithm.


This rule set suggests using a pointer in this case. I mentioned that
updating and output by pointers would be indistinguishable.


Okay, I see what you're saying.
Generally, if you believe that functions which operate on a objects
of a given
type should be non-members as often as possible,


(I certainly do agree.)
then most of the functions
which would otherwise be non-const member functions will become
non-member functions taking a reference to non-const.

Jonathan


There is no reason to make functions into member functions to avoid
non-const references.


I wouldn't dream of it. ;-) Often it's impossible, anyway, since you don't
control the class definition.
Use pointers:

func3(&obj); // outputting an object

Again, outputting and updating are not distinguishable.


Having to sprinkle ampersands all around creates a lot of noise, IMO. I'd
rather give functions descriptive names:

int n = read_int32(std::cin, flags);

sort(vec, by_age());

Jonathan
Jul 23 '05 #27
In message <cu**********@nntp1.jpl.nasa.gov>, E. Robert Tisdale
<E.**************@jpl.nasa.gov> writes
Richard Herring wrote:
In message <ct**********@nntp1.jpl.nasa.gov>, E. Robert Tisdale
<E.**************@jpl.nasa.gov> writes
Berk Birand wrote:

Also another question that pops in my mind mind
is whether to use references or pointers in function headers.
Is it better practice to declare a function as, say:
void afunc(int& i);
Or is it better to declare it as
void afunc(int* i);
I know that, in the second case,
one has to explicitly dereference i whenever one wants to access it.
But this can be considered a good thing,
as it makes it more obvious that
you are manipulating pointers, and the values are going to change.
On the other hand, it's just more work!
Avoid functions that modify their arguments.
Pass a const reference and return a result by value. Write

int afunct(const int& i);

or

int afunct(const int* p);

instead.

Would you give the same advice if the object being modified were a
million-element vector?


Yes.

Elaborate a little. An example might help.
You might be surprised


struct I {
int i_;
}

typedef std::vector<I> Vector;

/* Function which sets element p of a Vector to value q
As recommended, function doesn't modify its arguments
but returns a copy.
*/
Vector afunct (const Vector & v, int p, int q)
{
Vector temp(v);
temp[p].i_ = q;
return temp;
}

int main()
{
Vector v(1000000);
for (int i=0; i<1000000; ++i)
{
v = afunct(v, p, q);
}
/*...*/
}

Still think it's a good idea?

--
Richard Herring
Jul 23 '05 #28
[oops - hit post too soon :-( Second attempt:]

In message <cu**********@nntp1.jpl.nasa.gov>, E. Robert Tisdale
<E.**************@jpl.nasa.gov> writes
Richard Herring wrote:
In message <ct**********@nntp1.jpl.nasa.gov>, E. Robert Tisdale
<E.**************@jpl.nasa.gov> writes
Berk Birand wrote:

Also another question that pops in my mind mind
is whether to use references or pointers in function headers.
Is it better practice to declare a function as, say:
void afunc(int& i);
Or is it better to declare it as
void afunc(int* i);
I know that, in the second case,
one has to explicitly dereference i whenever one wants to access it.
But this can be considered a good thing,
as it makes it more obvious that
you are manipulating pointers, and the values are going to change.
On the other hand, it's just more work!
Avoid functions that modify their arguments.
Pass a const reference and return a result by value. Write

int afunct(const int& i);

or

int afunct(const int* p);

instead.

Would you give the same advice if the object being modified were a
million-element vector?


Yes.

Elaborate a little. An example might help.
You might be surprised


struct I {
int i_;
/* and maybe some other stuff */
}

typedef std::vector<I> Vector;

/* Function which sets element p of a Vector to value q
As recommended, function doesn't modify its arguments
but returns a copy.
*/
Vector afunct (const Vector & v, int p, int q)
{
Vector temp(v);
temp[p].i_ = q;
return temp;
}

int main()
{
Vector v(1000000);
for (int i=0; i<1000000; ++i)
{
v = afunct(v, i, i);
}
/*...*/
}

Still think it's a good idea?

--
Richard Herring
Jul 23 '05 #29
Richard Herring wrote:
E. Robert Tisdale writes
Richard Herring wrote:
Would you give the same advice
if the object being modified were a million-element vector?
Yes.

Elaborate a little. An example might help.
You might be surprised

struct I {
int i_;
/* and maybe some other stuff */
}

typedef std::vector<I> Vector;

/* Function which sets element p of a Vector to value q
As recommended, function doesn't modify its arguments
but returns a copy.
*/

Vector afunct (const Vector & v, int p, int q) {
Vector temp(v);
temp[p].i_ = q;
return temp;
}

int main(int argc, char* argv[]) {

Vector v(1000000);
for (int i = 0; i < 1000000; ++i) {
v = afunct(v, i, i);
}
/*...*/

return 0; }

Still think it's a good idea?
I'm thinking more like
cat main.cc

#include <cmath>
#include <vector>
#include <iostream>

std::vector<double> ramp(size_t n) {
std::vector<double> x(n);
for (size_t j = 0; j < x.size(); ++j)
x[j] = j;
return x;
}

std::vector<double> sqrt(const std::vector<double>& x) {
std::vector<double> y(x.size());
for (size_t j = 0; j < x.size(); ++j)
y[j] = sqrt(x[j]);
return y;
}

std::ostream& operator<<(std::ostream& os,
const std::vector<double>& x) {
for (size_t j = 0; j < x.size(); ++j)
os << ' ' << x[j];
return os << std::endl;
}

int
main(int argc, char* argv[]) {
const
size_t n = 4;
const
std::vector<double> x = ramp(n);
std::cout << "x =" << std::endl;
std::cout << x << std::endl;
const
std::vector<double> y = sqrt(x);
std::cout << "x =" << std::endl;
std::cout << y << std::endl;
return 0;
}

The problem with your example is that
you have not justified modifying std::vector<I> v *in-place*.
You contrived a ridiculous *straw-man* function afunct
then *assign* the return value to v a million times!
Didn't you notice that assignment modifies v?

Some objects (I/O streams, for example) are not useful
unless they can be modified.
Containers often must be declared as variables
and modified in place (as in the ramp and sqrt functions above).
But most other object including most containers
can be defined as constants initialized with a function.

I think that most experience programmers agree that
it is important to minimize the number of [state] variables
in your program because it makes your programs easier
to analyze (read, understand and maintain).
So my advice is,
"Avoid defining functions that modify their arguments
because they compel programmers to declare an pass variables
which may otherwise be constants in the rest of the program.
Reserve functions that modify their arguments
to situations where an object *must* be modified in-place."

For example, I think that almost everybody would agree that

void sqrt(std::vector<double>& x) {
std::vector<double> y(x.size());
for (size_t j = 0; j < x.size(); ++j)
x[j] = sqrt(x[j]);
}

would probably be a bad design.
Jul 23 '05 #30
Siemel Naran wrote:
E. Robert Tisdale wrote:
Siemel Naran wrote:

If one is allowed to pass in a NULL integer (not zero, which is
something),
meaning that 'i' is not supplied, then you have to choose the second
version. Otherwise prefer the first method as it makes it clear that
the
caller has to pass in a valid integer.


A better option would be to overload the function

void afunc(void);

"to pass in a NULL integer".

Say you call a function find(x,y) that returns a pointer to an integer, and
NULL if the integer does not exist. Then one could just say

afunc(find(x, y));

where the signature of afunc is void afunc(int *).
Surely this is more convenient than

int * f = find(x, y);
if (f) afunc(*f);
else afunc();


Would it be more convenient
if find(x, y) returned an array subscript instead of a pointer?

Jul 23 '05 #31

E. Robert Tisdale wrote:
Richard Herring wrote:
E. Robert Tisdale writes
Richard Herring wrote:

Would you give the same advice
if the object being modified were a million-element vector?

Yes.

Elaborate a little. An example might help.
You might be surprised

struct I {
int i_;
/* and maybe some other stuff */
}

typedef std::vector<I> Vector;

/* Function which sets element p of a Vector to value q
As recommended, function doesn't modify its arguments
but returns a copy.
*/

Vector afunct (const Vector & v, int p, int q) {
Vector temp(v);
temp[p].i_ = q;
return temp;
}

int main(int argc, char* argv[]) {

Vector v(1000000);
for (int i = 0; i < 1000000; ++i) {
v = afunct(v, i, i);
}
/*...*/

return 0;
}

Still think it's a good idea?

I'm thinking more like
> cat main.cc

#include <cmath>
#include <vector>
#include <iostream>

std::vector<double> ramp(size_t n) {
std::vector<double> x(n);
for (size_t j = 0; j < x.size(); ++j)
x[j] = j;
return x;
}

std::vector<double> sqrt(const std::vector<double>& x) {
std::vector<double> y(x.size());
for (size_t j = 0; j < x.size(); ++j)
y[j] = sqrt(x[j]);
return y;
}

std::ostream& operator<<(std::ostream& os,
const std::vector<double>& x) {
for (size_t j = 0; j < x.size(); ++j)
os << ' ' << x[j];
return os << std::endl;
}

int
main(int argc, char* argv[]) {
const
size_t n = 4;
const
std::vector<double> x = ramp(n);
std::cout << "x =" << std::endl;
std::cout << x << std::endl;
const
std::vector<double> y = sqrt(x);
std::cout << "x =" << std::endl;
std::cout << y << std::endl;
return 0;
}

The problem with your example is that
you have not justified modifying std::vector<I> v *in-place*.
You contrived a ridiculous *straw-man* function afunct
then *assign* the return value to v a million times!
Didn't you notice that assignment modifies v?

This isn't the point. The point is that it's best not to return a
large object by value, that's all. If you really need to "justify
modifying std::vector<I> v *in-place*" then you should set 'n' to
10000000 in your code above, you will probably notice a difference in
performance.

Some objects (I/O streams, for example) are not useful
unless they can be modified.

Of course.

So my advice is,
"Avoid defining functions that modify their arguments
because they compel programmers to declare an pass variables
which may otherwise be constants in the rest of the program.
Reserve functions that modify their arguments
to situations where an object *must* be modified in-place."

This is good advice in most cases, but it cannot be used for all cases.
Obviously if you have *very* large objects, you must take a different
approach.

For example, I think that almost everybody would agree that

void sqrt(std::vector<double>& x) {
std::vector<double> y(x.size());
for (size_t j = 0; j < x.size(); ++j)
x[j] = sqrt(x[j]);
}

would probably be a bad design.

Yes, I agree. For many reasons, none of which have anything to do with
this thread. Why do we need 'y'? And why do we need a sqrt function
that takes 'std::vector<double>&'? What's wrong with plain old 'double
sqrt(double)' and using 'for_each' to apply it to each element in a
vector (or *any* container).

Hope this helps,
-shez-

Jul 23 '05 #32
Shezan Baig wrote:
E. Robert Tisdale wrote:
Richard Herring wrote:
E. Robert Tisdale writes

Richard Herring wrote:

>Would you give the same advice
>if the object being modified were a million-element vector?

Yes.

Elaborate a little. An example might help.
You might be surprised

struct I {
int i_;
/* and maybe some other stuff */
}

typedef std::vector<I> Vector;

/* Function which sets element p of a Vector to value q
As recommended, function doesn't modify its arguments
but returns a copy.
*/

Vector afunct (const Vector & v, int p, int q) {
Vector temp(v);
temp[p].i_ = q;
return temp;
}

int main(int argc, char* argv[]) {

Vector v(1000000);
for (int i = 0; i < 1000000; ++i) {
v = afunct(v, i, i);
}
/*...*/


return 0;
}

Still think it's a good idea?


I'm thinking more like
> cat main.cc

#include <cmath>
#include <vector>
#include <iostream>

std::vector<double> ramp(size_t n) {
std::vector<double> x(n);
for (size_t j = 0; j < x.size(); ++j)
x[j] = j;
return x;
}

std::vector<double> sqrt(const std::vector<double>& x) {
std::vector<double> y(x.size());
for (size_t j = 0; j < x.size(); ++j)
y[j] = sqrt(x[j]);
return y;
}

std::ostream& operator<<(std::ostream& os,
const std::vector<double>& x) {
for (size_t j = 0; j < x.size(); ++j)
os << ' ' << x[j];
return os << std::endl;
}

int
main(int argc, char* argv[]) {
const
size_t n = 4;
const
std::vector<double> x = ramp(n);
std::cout << "x =" << std::endl;
std::cout << x << std::endl;
const
std::vector<double> y = sqrt(x);
std::cout << "x =" << std::endl;
std::cout << y << std::endl;
return 0;
}

The problem with your example is that
you have not justified modifying std::vector<I> v *in-place*.
You contrived a ridiculous *straw-man* function afunct
then *assign* the return value to v a million times!
Didn't you notice that assignment modifies v?


This isn't the point. The point is that
it's best not to return a large object by value, that's all.
If you really need to "justify modifying std::vector<I> v *in-place*"
then you should set 'n' to 10000000 in your code above,
you will probably notice a difference in performance.


I don't think you said what you meant to say here.
Of course it will take about 2.5 million times as long
to process 10 million elements as it does to process 4 elements.

I suspect that you meant that

void sqrt(std::vector<double>& x) {
std::vector<double> y(x.size());
for (size_t j = 0; j < x.size(); ++j)
x[j] = sqrt(x[j]);
}

would be faster than

std::vector<double> sqrt(const std::vector<double>& x) {
std::vector<double> y(x.size());
for (size_t j = 0; j < x.size(); ++j)
y[j] = sqrt(x[j]);
return y;
}

If that's what you meant, you're wrong.
But don't take my word for it.
Try it yourself.
So my advice is,
"Avoid defining functions that modify their arguments
because they compel programmers to declare an pass variables
which may otherwise be constants in the rest of the program.
Reserve functions that modify their arguments
to situations where an object *must* be modified in-place."


This is good advice in most cases, but it cannot be used for all cases.
Obviously, if you have *very* large objects,
you must take a different approach.


No. you are wrong. Try it.
For example, I think that almost everybody would agree that

void sqrt(std::vector<double>& x) {
std::vector<double> y(x.size());
for (size_t j = 0; j < x.size(); ++j)
x[j] = sqrt(x[j]);
}

would probably be a bad design.


Yes, I agree. For many reasons,

none of which have anything to do with this thread.
Why do we need 'y'?
And why do we need a sqrt function
that takes 'std::vector<double>&'?
What's wrong with plain old 'double sqrt(double)' and using 'for_each'
to apply it to each element in a vector (or *any* container).


If it isn't obvious to you,
provide an example showing what you mean to do.
Note that your solution would compel the programmer
to declare a variable vector (y) to receive the result
even if y is never modified anywhere else in the program.
Jul 23 '05 #33
E. Robert Tisdale wrote:

This isn't the point. The point is that
it's best not to return a large object by value, that's all.
If you really need to "justify modifying std::vector<I> v *in-place*"
then you should set 'n' to 10000000 in your code above,
you will probably notice a difference in performance.
I don't think you said what you meant to say here.
Of course it will take about 2.5 million times as long
to process 10 million elements as it does to process 4 elements.

I suspect that you meant that

void sqrt(std::vector<double>& x) {
std::vector<double> y(x.size());

Why would you need this 'y' vector???? I suspect this is why you don't
notice the difference in performance.

for (size_t j = 0; j < x.size(); ++j)
x[j] = sqrt(x[j]);
}

would be faster than

std::vector<double> sqrt(const std::vector<double>& x) {
std::vector<double> y(x.size());
for (size_t j = 0; j < x.size(); ++j)
y[j] = sqrt(x[j]);
return y;
}

If that's what you meant, you're wrong.
But don't take my word for it.
Try it yourself.

Just for the sake of argument, I tried it. There is a big difference
when you are running with debugging information (no optimisation).
When you switch on optimisation, the difference is reduced - but still
not the same. If you look at the code (and my comment above), it's
obvious why its not the same.

Personally, I would prefer good performance even in debug builds. It
just makes maintenance easier :)

So my advice is,
"Avoid defining functions that modify their arguments
because they compel programmers to declare an pass variables
which may otherwise be constants in the rest of the program.
Reserve functions that modify their arguments
to situations where an object *must* be modified in-place."


This is good advice in most cases, but it cannot be used for all cases.
Obviously, if you have *very* large objects,
you must take a different approach.


No. you are wrong. Try it.

I tried it. I still prefer taking a different approach :)
For example, I think that almost everybody would agree that

void sqrt(std::vector<double>& x) {
std::vector<double> y(x.size());
for (size_t j = 0; j < x.size(); ++j)
x[j] = sqrt(x[j]);
}

would probably be a bad design.


Yes, I agree. For many reasons,

none of which have anything to do with this thread.
Why do we need 'y'?
And why do we need a sqrt function
that takes 'std::vector<double>&'?
What's wrong with plain old 'double sqrt(double)' and using 'for_each' to apply it to each element in a vector (or *any* container).


If it isn't obvious to you,
provide an example showing what you mean to do.
Note that your solution would compel the programmer
to declare a variable vector (y) to receive the result
even if y is never modified anywhere else in the program.

I don't see anything wrong with this. I trust you are an experienced
enough programmer to know not to make 'y' visible anywhere else. Try
to focus on higher level issues (like scoping your data) and you'll
find that there is no real need to make 'y' const. And your code will
be cleaner too :)
Hope this helps,
-shez-

Jul 23 '05 #34
Shezan Baig wrote:
E. Robert Tisdale wrote:
This isn't the point. The point is that
it's best not to return a large object by value, that's all.
If you really need to "justify modifying std::vector<I> v
*in-place*"
then you should set 'n' to 10000000 in your code above,
you will probably notice a difference in performance.
I don't think you said what you meant to say here.
Of course it will take about 2.5 million times as long
to process 10 million elements as it does to process 4 elements.

I suspect that you meant that

void sqrt(std::vector<double>& x) {
std::vector<double> y(x.size());


Why would you need this 'y' vector?


You have discovered "a paste-o" in my code.
Please omit this object.
I suspect this is why you don't notice the difference in performance.
for (size_t j = 0; j < x.size(); ++j)
x[j] = sqrt(x[j]);
}

would be faster than

std::vector<double> sqrt(const std::vector<double>& x) {
std::vector<double> y(x.size());
for (size_t j = 0; j < x.size(); ++j)
y[j] = sqrt(x[j]);
return y;
}

If that's what you meant, you're wrong.
But don't take my word for it.
Try it yourself.
Just for the sake of argument, I tried it.


Please show us the code that you used.
There is a big difference
when you are running with debugging information (no optimisation).
When you switch on optimisation,
Please tell us which compiler and optimization options you used.
the difference is reduced - but still not the same.
Please show us the timing results.
If you look at the code (and my comment above),
it's obvious why its not the same.

Personally, I would prefer good performance even in debug builds.
It just makes maintenance easier :)


So do I. But passing a non-const reference
instead of returning by value won't improve performance.
So my advice is,
"Avoid defining functions that modify their arguments
because they compel programmers to declare an pass variables
which may otherwise be constants in the rest of the program.
Reserve functions that modify their arguments
to situations where an object *must* be modified in-place."This is good advice in most cases,
but it cannot be used for all cases.Obviously, if you have *very* large objects,
you must take a different approach.


No. you are wrong. Try it.


I tried it. I still prefer taking a different approach :)


You can lead a [horse] to water but ...
For example, I think that almost everybody would agree that

void sqrt(std::vector<double>& x) {
std::vector<double> y(x.size());
for (size_t j = 0; j < x.size(); ++j)
x[j] = sqrt(x[j]);
}

would probably be a bad design.

Yes, I agree. For many reasons,

none of which have anything to do with this thread.
Why do we need 'y'?
And why do we need a sqrt function
that takes 'std::vector<double>&'?
What's wrong with plain old 'double sqrt(double)' and using
'for_each'
to apply it to each element in a vector (or *any* container).


If it isn't obvious to you,
provide an example showing what you mean to do.
Note that your solution would compel the programmer
to declare a variable vector (y) to receive the result
even if y is never modified anywhere else in the program.


I don't see anything wrong with this.
I trust you are an experienced enough programmer
to know not to make 'y' visible anywhere else.
Try to focus on higher level issues
(like scoping your data) and you'll find that
there is no real need to make 'y' const.
And your code will be cleaner too :)


Please show us an example of what you mean.
I'm pretty sure that you're confused.
Jul 23 '05 #35

E. Robert Tisdale wrote:
If it isn't obvious to you,
provide an example showing what you mean to do.
Note that your solution would compel the programmer
to declare a variable vector (y) to receive the result
even if y is never modified anywhere else in the program.
I don't see anything wrong with this.
I trust you are an experienced enough programmer
to know not to make 'y' visible anywhere else.
Try to focus on higher level issues
(like scoping your data) and you'll find that
there is no real need to make 'y' const.
And your code will be cleaner too :)


Please show us an example of what you mean.


{
{ // <-- new scope
// declare y and use it (localise)
}

// "anywhere else in the program"
}
I'm pretty sure that you're confused.


Hope this helps,
-shez-

Jul 23 '05 #36
Shezan Baig wrote:
E. Robert Tisdale wrote:
Please show us an example of what you mean.
{
{ // <-- new scope
// declare y


// const
std::vector<double> x(n);

// x is defined as a variable
// only so that I can initialize it with
extern void sqrt(std::vector<double>&);

sqrt(x);

// Now, x is supposed to be constant.
// and use it (localize)
g(x);

// Does x still have the same value?
// Or did function g modify it?

x[0] = 13.0; // error! x is supposed to be constant

// The compiler won't catch this error.
}

// "anywhere else in the program"
}

Jul 23 '05 #37
In message <cu**********@nntp1.jpl.nasa.gov>, E. Robert Tisdale
<E.**************@jpl.nasa.gov> writes
Richard Herring wrote:
E. Robert Tisdale writes
Richard Herring wrote:
[restoring over-snipped context]
Avoid functions that modify their arguments.
Pass a const reference and return a result by value.

Would you give the same advice if the object being modified were a
million-element vector?

Yes.

Elaborate a little. An example might help.
You might be surprised struct I {
int i_;
/* and maybe some other stuff */
}
typedef std::vector<I> Vector;
/* Function which sets element p of a Vector to value q
As recommended, function doesn't modify its arguments
but returns a copy.
*/
Vector afunct (const Vector & v, int p, int q) {
Vector temp(v);
temp[p].i_ = q;
return temp;
}
int main(int argc, char* argv[]) {
Vector v(1000000);
for (int i = 0; i < 1000000; ++i) {
v = afunct(v, i, i);
}
/*...*/

return 0;
}
Still think it's a good idea?

[snip]
The problem with your example is that
you have not justified modifying std::vector<I> v *in-place*.
You contrived a ridiculous *straw-man* function afunct
Ridiculous, certainly, But it's not a strawman, it's a counter-example.
It's a function which precisely complies with your "recommendation" and
illustrates its absurdity:
Avoid functions that modify their arguments.
Pass a const reference and return a result by value.


.... which I notice you conveniently snipped from your reply.

You might want to check on the definition of "strawman" before you use
it again.
then *assign* the return value to v a million times!
Didn't you notice that assignment modifies v?


Of course I "noticed". That's precisely my point.

[...]

--
Richard Herring
Jul 23 '05 #38

E. Robert Tisdale wrote:
Shezan Baig wrote:
E. Robert Tisdale wrote:
Please show us an example of what you mean.


{
{ // <-- new scope
// declare y


// const
std::vector<double> x(n);

// x is defined as a variable
// only so that I can initialize it with
extern void sqrt(std::vector<double>&);

sqrt(x);

// Now, x is supposed to be constant.
// and use it (localize)


g(x);

// Does x still have the same value?
// Or did function g modify it?

x[0] = 13.0; // error! x is supposed to be constant

// The compiler won't catch this error.
}

// "anywhere else in the program"
}


If you really had code like this in your software, I would suggest
refactoring.

Hope this helps,
-shez-

Jul 23 '05 #39
Richard Herring wrote:
E. Robert Tisdale writes:
Richard Herring wrote:
E. Robert Tisdale writes

Richard Herring wrote:
[restoring over-snipped context]
Avoid functions that modify their arguments.
> Pass a const reference and return a result by value.
>
> Would you give the same advice
> if the object being modified were a million-element vector?

Yes.

Elaborate a little. An example might help.
You might be surprised

struct I {
int i_;
/* and maybe some other stuff */
}
typedef std::vector<I> Vector;
/* Function which sets element p of a Vector to value q
As recommended, function doesn't modify its arguments
but returns a copy.
*/ Vector afunct (const Vector & v, int p, int q) {
Vector temp(v);
temp[p].i_ = q;
return temp;
}
int main(int argc, char* argv[]) {
Vector v(1000000);
for (int i = 0; i < 1000000; ++i) {
v = afunct(v, i, i);
}
/*...*/
return 0;
}
Still think it's a good idea?
[snip]
The problem with your example is that
you have not justified modifying std::vector<I> v *in-place*.
You contrived a ridiculous *straw-man* function afunct


Ridiculous, certainly.
But it's not a strawman, it's a counter-example.
It's a function which precisely complies
with your "recommendation" and illustrates its absurdity:
Avoid functions that modify their arguments.
> Pass a const reference and return a result by value.
... which I notice you conveniently snipped from your reply.


It was redundant given the quote that you snipped:
"Avoid defining functions that modify their arguments
because they compel programmers to declare an pass variables
which may otherwise be constants in the rest of the program.
Reserve functions that modify their arguments
to situations where an object *must* be modified in-place."

If I am missing your point, please elaborate.
You might want to check on the definition of "strawman"
before you use it again.


http://www.don-lindsay-archive.org/s...nts.html#straw
then *assign* the return value to v a million times!
Didn't you notice that assignment modifies v?


Of course I "noticed". That's precisely my point.


Why, then, couldn't you avoid the assignment statement?
You never told us that.
Jul 23 '05 #40
Shezan Baig wrote:
E. Robert Tisdale wrote:
Shezan Baig wrote:
E. Robert Tisdale wrote:

Please show us an example of what you mean.

{
{ // <-- new scope
// declare y


// const
std::vector<double> x(n);

// x is defined as a variable
// only so that I can initialize it with
extern void sqrt(std::vector<double>&);

sqrt(x);

// Now, x is supposed to be constant.

// and use it (localize)


g(x);

// Does x still have the same value?
// Or did function g modify it?

x[0] = 13.0; // error! x is supposed to be constant

// The compiler won't catch this error.
}

// "anywhere else in the program"
}


If you really had code like this in your software,
I would suggest refactoring.


Please show us how *you* would "refactor" the code above.
Jul 23 '05 #41
In message <cu**********@nntp1.jpl.nasa.gov>, E. Robert Tisdale
<E.**************@jpl.nasa.gov> writes
Richard Herring wrote:
E. Robert Tisdale writes:
Richard Herring wrote:

E. Robert Tisdale writes

> Richard Herring wrote: [restoring over-snipped context]
>> Avoid functions that modify their arguments.
>> Pass a const reference and return a result by value.
>>
>> Would you give the same advice
>> if the object being modified were a million-element vector?
>
> Yes.
>
> Elaborate a little. An example might help.
> You might be surprised

struct I {
int i_;
/* and maybe some other stuff */
}
typedef std::vector<I> Vector;
/* Function which sets element p of a Vector to value q
As recommended, function doesn't modify its arguments
but returns a copy.
*/

Vector afunct (const Vector & v, int p, int q) {
Vector temp(v);
temp[p].i_ = q;
return temp;
}
int main(int argc, char* argv[]) {
Vector v(1000000);
for (int i = 0; i < 1000000; ++i) {
v = afunct(v, i, i);
}
/*...*/

return 0;

}
Still think it's a good idea?


[snip]
The problem with your example is that
you have not justified modifying std::vector<I> v *in-place*.
You contrived a ridiculous *straw-man* function afunct

Ridiculous, certainly.
But it's not a strawman, it's a counter-example.
It's a function which precisely complies
with your "recommendation" and illustrates its absurdity:
>> Avoid functions that modify their arguments.
>> Pass a const reference and return a result by value.

... which I notice you conveniently snipped from your reply.


It was redundant given the quote that you snipped:
"Avoid defining functions that modify their arguments
because they compel programmers to declare an pass variables
which may otherwise be constants in the rest of the program.
Reserve functions that modify their arguments
to situations where an object *must* be modified in-place."


No. That quote is a modification of your original untenable position.
If I am missing your point, please elaborate.
In message <ct**********@nntp1.jpl.nasa.gov>
you posted a categorical, no-exceptions imperative statement:

"Avoid functions that modify their arguments.
Pass a const reference and return a result by value."

In <Fu**************@baesystems.com> I asked

"Would you give the same advice if the object being modified were a
million-element vector?"

In <cu**********@nntp1.jpl.nasa.gov> you replied

"Yes".

HTH.
You might want to check on the definition of "strawman"
before you use it again.
http://www.don-lindsay-archive.org/s...nts.html#straw


Very good. Which part of "Avoid functions that modify their arguments.
Pass a const reference and return a result by value." did I exaggerate
or caricature?
then *assign* the return value to v a million times!
Didn't you notice that assignment modifies v?

Of course I "noticed". That's precisely my point.


Why, then, couldn't you avoid the assignment statement?
You never told us that.


If a function is forbidden to modify its arguments, how else do you
propose that calling it should cause a modification?

--
Richard Herring
Jul 23 '05 #42

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
by: Seeker | last post by:
Newbie question here... I have a form with some radio buttons. To verify that at least one of the buttons was chosen I use the following code ("f" is my form object) : var btnChosen; for...
6
by: Kenny | last post by:
Hello, can anyone tell me how to pass an array to a function ? I have this function , part of my class. It works if I do not put in int a everywhere , but obviously , I need to add an array so I...
1
by: Mark Dicken | last post by:
Hi All I have found the following Microsoft Technet 'Q' Article :- Q210368 -ACC2000: How to Pass an Array as an Argument to a Procedure (I've also copied and pasted the whole contents into...
5
by: wilson | last post by:
Dear all, In this time, I want to pass array to function. What should I declare the parameter in the function?i int array or int array? Which one is correct? ...
10
by: nospam | last post by:
Hello! I can pass a "pointer to a double" to a function that accepts double*, like this: int func(double* var) { *var=1.0; ... }
9
by: Alan Silver | last post by:
Hello, I'm a bit surprised at the amount of boilerplate code required to do standard data access in .NET and was looking for a way to improve matters. In Classic ASP, I used to have a common...
14
by: Abhi | last post by:
I wrote a function foo(int arr) and its prototype is declared as foo(int arr); I modify the values of the array in the function and the values are getting modified in the main array which is...
3
by: QQ | last post by:
I have one integer array int A; I need to pass this array into a function and evaluate this array in this function how should I pass? Is it fine? void test(int *a)
4
by: IRC | last post by:
hey, i am pretty new on javascript as well as PHP, Hey, anyone can you help me, how to pass the javascript array value to php page......... i want to retrieve the values which are arrayed on...
11
by: venkatagmail | last post by:
I have problem understanding pass by value and pass by reference and want to how how they are or appear in the memory: I had to get my basics right again. I create an array and try all possible...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.