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 41 8085
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
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
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?
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;
}
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. 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!
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;
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.
"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.
"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);
}
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
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
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-
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
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
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
"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
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.
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
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
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".
"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/
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
"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();
"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/
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
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
[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
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.
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?
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-
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.
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-
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.
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-
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" }
In message <cu**********@nntp1.jpl.nasa.gov>, E. Robert Tisdale
<E.**************@jpl.nasa.gov> writes Richard Herring wrote: E. Robert Tisdale writesRichard 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
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-
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.
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.
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 This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
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...
|
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...
|
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
...
|
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...
|
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;
...
}
|
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...
|
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...
|
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...
|
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...
|
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...
|
by: tammygombez |
last post by:
Hey fellow JavaFX developers,
I'm currently working on a project that involves using a ComboBox in JavaFX, and I've run into a bit of an issue....
|
by: tammygombez |
last post by:
Hey everyone!
I've been researching gaming laptops lately, and I must say, they can get pretty expensive. However, I've come across some great...
|
by: better678 |
last post by:
Question:
Discuss your understanding of the Java platform. Is the statement "Java is interpreted" correct?
Answer:
Java is an object-oriented...
|
by: CD Tom |
last post by:
This only shows up in access runtime. When a user select a report from my report menu when they close the report they get a menu I've called Add-ins...
|
by: Naresh1 |
last post by:
What is WebLogic Admin Training?
WebLogic Admin Training is a specialized program designed to equip individuals with the skills and knowledge...
|
by: antdb |
last post by:
Ⅰ. Advantage of AntDB: hyper-convergence + streaming processing engine
In the overall architecture, a new "hyper-convergence" concept was...
|
by: Matthew3360 |
last post by:
Hi there. I have been struggling to find out how to use a variable as my location in my header redirect function.
Here is my code.
...
|
by: Matthew3360 |
last post by:
Hi, I have a python app that i want to be able to get variables from a php page on my webserver. My python app is on my computer. How would I make it...
|
by: AndyPSV |
last post by:
HOW CAN I CREATE AN AI with an .executable file that would suck all files in the folder and on my computerHOW CAN I CREATE AN AI with an .executable...
| |