Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
The community reviewed whether to reopen this question 8 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I was coding up a C++ class today, and I wrote a function that took an argument as a reference rather than a pointer, something I rarely ever do. I've always passed by pointers. So I was about to change it back, and then I realized - I have no idea if I should, or if it even matters.
So I turn to you guys. I have three ways of passing parameters about:
//1: By pointer
Object* foo(Object* bar) {…}
//2: By reference
Object& foo(Object& bar) {…}
//3: By value (just for completeness)
Object foo(Object bar) {…}
Assuming #3's out for performance reasons (yes, I know compilers have gotten pretty good at this, but still), the other two are more or less equivalent.
So: What's the "best" method? Pointers? References? Some combination of the two? Or does it even matter? Technical reasons are the best, but stylistic reasons are just as good.
Update: I've accepted YeenFei's answer, since it deals with the difference that clinched it for me (even if I then pointedly ignored his advice - I like having NULL as an option...). But everyone made good points - especially GMan (in the comments), and Nemo, in the answer dealing with performance and passing by value. If you're here for answers, check them all!
I would suggest to pass your argument by reference if it is expected to be valid. This would be a by-design optimization and save you from defensive programming.
Reference cannot be null while pointer can.
If you are dealing with pointer, you will need to verify whether given pointer is valid (non-null) regardless it is in raw form or wrapped in managed container (shared_ptr), before using them.
So I am going to make the case for choice #3. Consider the following code:
struct Foo {
int x;
int y;
};
Foo
add(Foo a, Foo b)
{
Foo result;
result.x = a.x + b.x;
result.y = a.y + b.y;
return result;
}
Foo
add2(Foo &a, Foo &b)
{
Foo result;
result.x = a.x + b.x;
result.y = a.y + b.y;
return result;
}
Try examining the generated assembly. Notice how add is almost entirely register operations, nicely scheduled. Notice how add2 is lots of memory accesses without any reordering.
I wrote a main that called each of these functions 10 billion times. Result? add took 22 seconds, while add2 took 26 seconds. Even for this trivial example, that's 10-20% better performance for the pass-by-value version.
OK, so the structure is trivial. But so is the function. The more complex the function, the more likely the pass-by-value version is to be faster, because the compiler knows that the two arguments do not "overlap". This is a huge benefit to optimization.
Of course, this decision should primarily be based on the semantics of the function: Do you need NULL to be a legal value? If so, obviously you need a pointer. Do you need to modify the objects? Then use a pointer or a reference.
But if you do not need to modify the objects, prefer to pass them by value unless the objects are large and/or have a non-trivial copy constructor (e.g. std::string). If by-value really is too slow, pass by reference-of-const or pointer-to-const.
But do not underestimate the potential speed advantages of passing by value, which derive from the advantages of registers vs. memory and instruction reordering. And note that these advantages become more pronounced with every generation of CPU.
Passing by pointer and by reference are really the same, except in syntax. I prefer passing by pointer, because it makes things explicit:
Object bar;
ptr_foo(&bar); // bar may change
ref_foo(bar); // can bar change? Now I need to go look at the prototype...
val_foo(bar); // bar cannot change. (Unless you use references here and there)
The only technical preference between passing values and pointers, as you have touched on is if the class is large enough to make its passing slow.
References any day, if you're designing everything yourself. Idiomatic modern C++ should almost never have raw pointers sticking out anywhere. Dynamically allocated objects should travel in resource managing containers (shared_ptr or unique_ptr, or weak_ptr if applicable), but for most operations passing by (const) reference is the primary way to pass arguments that need to be modified or that are of a heavy-weight type. Don't forget that passing by value may be a viable option if you have movable types.
Use:
const reference if the object is not modified
pointer if the object is modified or can be null
value if the object is small and you care about performance or if you need a copy of the object inside the function. This allows the compiler to pick the best way to copy/move the argument.
std::unique_ptr if ownership is transferred to the function.
You can take a look at https://www.boost.org/doc/libs/1_51_0/libs/utility/call_traits.htm library, it converts the type to the best arguments type automatically.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 months ago.
Improve this question
I was studying about advanced topics, and came across a question in my mind - why would I need to use pass-by-value instead of pass-by-reference?
The question is originated around memory management, which is, if passing a variable as a value into a function makes a copy of the variable, shouldn't I need to pass it by reference as const to efficiently use memory?
When I researched on Google, it generally stated that pass-by-value should be used when we don't want to change the variable inside of a function.
Here is my example below:
#include <iostream>
void foo(int a) {
std::cout << "value of a: " << a << std::endl;
}
int main(){
int b = 5;
foo(5);
return EXIT_SUCCESS;
}
In the code block above, I passed b as value into the function foo(). Which made a copy of b.
Instead, if I write the function like below:
#include <iostream>
void foo(const int& a) {
std::cout << "value of a: " << a << std::endl;
}
int main(){
int b = 5;
foo(5);
return EXIT_SUCCESS;
}
With this code block, I don't make a copy of b.
What are the advantages of using pass-by-value when I am calling functions instead of pass-by-reference, besides when I don't want to change the content of the variable inside of a function?
For larger or more complex custom types, making a full and deep copy is not a trivial task. A classic example might be an array containing 1 million elements. If we pass it into the function by value, at the point of the call, all 1 million elements in that array need to be copied so that you can use them in the function. Most of the time, such a copy would be unnecessary and we can save a lot of processing by simply passing by reference.
I'll also add the note that if passing by reference, it is almost always best to pass by const reference unless you explicitly intend to modify the variable inside the function. This is for several reasons, three of which are:
It signals to everyone else that you don't want to modify it, and forbids you from doing so accidentally.
const variables can only be passed by const reference, so it allows your function to use both const and mutable (non-const) parameters.
rvalue references can bind to const lvalue references but not mutable ones, meaning that functions with const (lvalue) reference parameters can take temporaries and literals as parameters.
As is usually the case with C++, there are exceptions. Fundamental types like int, double, bool, etc are optimised to be passed by value on most modern systems, meaning that passing them by reference unnecessarily can be a performance downgrade, not upgrade. The rule of thumb I recommend for them is to only pass by reference if you have a specific extraneous reason for doing so.
I'll also add the caveat that passing by reference is to avoid unnecessary copies. There are situations out there where making a copy or passing by value is the best call and what makes the most sense. In those situations, don't be zealous and try to cheat your code into passing by reference if it really should be passing by value.
Passing integers by value is usually better for both performance and codesize.
The reason is that passing arguments usually happens through registers, which are faster than memory. If you pass a pointer, then unless the compiler inlines (which it often can't do and often won't do because it isn't a good idea), you effectively force the compiler to spill into memory what probably was held in a register. Those memory accesses will cost and code will need to be generated to do the spill and to load the address.
The difference isn't that great. I'm measuring it at about 0.3 ns per call in favor of passing integers by value (I got calls taking ints by value taking 1.5ns and calls taking ints by pointer/ref at 1.8ns). For larger objects that wouldn't be passed through registers (on Linux's x86-64 SysV that means for objects larger than 2 longs), passing by pointer/reference starts being a big win and the bigger, the larger the object.
As you know already, by reference parameters save you a copy, which is an advantage unless, as mentioned in some comments, you pass by reference a plain old data type. As soon as you are not passing ints/doubles/chars/Foo*s etc, you want to pass by reference:
most likely by const&, because if you were "happy" with passing by value, then you were not trying to modify the argument at the call site in the first place, so with const& you're ensuring that still holds, and you save the copy;
or & if you really want to modify the argument at the call site, but you're already doing that where you needed, because pass-by-value didn't let you do what you needed;
and then there's && (rvalue reference, not forwarding reference) for when you want to avoid the copy of the argument but forcing the caller to pass you a temporary (or a thing they are happy to hand over to you via std::move) that you can mess up with.
Forwarding references, T&& with T being a template parameter, are used too, but for that I would recommend reading this.
So, in any case, you save a copy.
And that is only one advantage. Another advantage is that you can't pass non-copyable classes by copy.¹
So no, unless you're talking of plain old data types, you really never prefer pass-by-value over pass-by-reference.
(¹) Despite std::unique_ptr is a non-copyable class, it doesn't really represent an example of what you'd prefer to pass by reference. What you'd want to do is to move its value into the callee (to say "dear callee, caller speaking: you own this thing now, not me"); in other words, the caller would std::move the std::unique_ptr<T> and the callee would take it by std::unique_ptr<T>, i.e. by value.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Everytime I think about the design of my class I ask myself these questions, should I use the pass by value, should I overload on const lvalue reference and rvalue reference or should I use perfect forwarding.
Often I use pass by value as having cheap to move types and I almost never use perfect forwarding. I overload when having only 1 parameter, maybe 2 if i really need the perf.
What do you do ?
Do you have easy rules of thumb to decide how to pass arguments, for member/non member functions but also for constructors and all the copy/assignment guys.
Thanks.
So all of the following is opinion-based, but these are the rules I tend to follow when thinking about an API. As always in C++, there are many ways to accomplish the same thing, and people will have different view on exactly what is best.
There are three kinds of parameters we need to think about: in parameters, out parameters, and in/out parameters. The latter two are simple, so we'll cover them first.
Out parameters
Don't use them. Seriously. If your function is going to return a new object, then return it by value. If you're going to return multiple new objects, then return them by value packed in a std::tuple (or std::pair). The caller can use std::tie (or structured bindings in C++17) to unpack them again. This gives the caller the maximum flexibility, and with RVO it's no less efficient than any other method.
In/out parameters
For functions which modify an already-constructed value, use a mutable lvalue reference, i.e. T&. This will prevent callers from passing a temporary, but that's actually a good thing: what would be the point of modifying something you're just going to throw away? Not that some style guides (notably Google's, but also Qt) advocate using a raw pointer (T*) in this situation, so that it's obvious at the call site that the argument will be modified (because you need to say f(&arg)), but I personally don't find this convincing.
In parameters
For pure input parameters, where the function will not modify the argument passed to it, things are a tiny bit more complicated. In general, the best advice is to pass by lvalue-reference-to-const, that is, const T&. This will allow the caller to pass both lvalues and rvalues. However, for small objects (sizeof(T) <= sizeof(void*)), such as int, it can be more efficient to pass by value instead.
An exception though is if you're going to take a copy of a passed argument, for example in a constructor; in this case, it's better to take the parameter by value, because the compiler can turn this into a move for rvalues.
What about T&&?
There are two circumstances where it's appropriate to use arguments of the form T&&. The first is templated forwarding functions where the type of the parameter is the template type, i.e.
template <typename T>
decltype(auto) func(T&& arg) {
return other_func(std::forward<T>(arg));
}
In this case, although the parameter looks as if it's an rvalue reference, it's actually a forwarding reference (sometimes called a universal reference). Only use a forwarding reference to pass things on to another function via std::forward; if you care about the value category of the argument, then T&& is not appropriate.
The second case is for real rvalue references, where the argument type is not a template parameter. In a very limited number of cases, it can be appropriate to overload on both the const arg& and arg&& forms, to avoid an unnecessary move. This should only be necessary in performance-critical situations in which you're going to copy or move the argument somewhere (for example, std::vector does this for its push_back() method) -- in general I would say it's better to take the argument by value and then move it into place.
Interfaces should express intent.
Optimisations should happen when users complain.
To me, the following interfaces have different meanings:
void foo(thing const& t); // "I won't modify your t. If it's copyable, I might copy it, but that's none of your concern."
void foo(thing t); // "Pass me a copy if you wish, or a temporary, or move your thing into me. What I do with t is up to me".
void foo(thing& t); // "t will be modified."
What follows now is only for "default" behavior. Like "normal" not really big types ("normal sized" vectors, strings etc.) nothing which seems to be very expensive in the first place.
In short:
Do whatever you like but be consistent.
There is no best practice which can guarantee you the best performance.
Some detail to this:
I was once on a conference having 3 popular C++ people (Herb Sutter, Andrei Alexandrescu and Scott Meyers) discuss this problem and each had another opinion on the best "default" behavior.
All by const-reference or by perfect-forwarding or just by-value.
So you won't get a perfect answer here. Compilers also can optimize differently etc.
Here is my personal opinion on this:
What I do is I prefer the by-value approach and if I later notice some thing becoming slow I start to optimize. I assume modern compilers being smart enough to avoid unnecessary copies and also maybe just move the object when they see it's no longer used afterwards. I try to keep in mind Return Value Optimization to let the compiler more easier optimize here if necessary (either return only one object or only r-values).
Though I have heard this behavior and optimization potential changing from compiler to compiler. So like said before: use what you prefer / stick to one way so it's consistent.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
When I was learning C++, I was told that if you want to have multiple classes reference the same object, you should give both a pointer to the object. In Modern C++, I'd probably interpret this as the object being a unique_ptr and the classes holding non-owning-raw-pointers.
Recently, my mentor at work suggested that you should only use pointers when you plan on having the store point to a different object or null at some point. Instead, you should store references in classes.
Reference member variables are a thing I've actually never seen before, so I was looking for advice on what the concerns were... It makes sense... You're saying that this reference is assumed to never be null... I guess the concern would then be that you couldn't /check/ for null. It would have to be an invariant of your class...
How about how this applies to using the references for polymorphism?
Update:
The answer that I selected covers my questions pretty well, but I thought I'd clarify for future readers. What I was really looking for was an idea of the consequences of using a reference rather than a pointer as a class member. I realise that the way the question was phrased made it sound more like I was looking for opinions on the idea.
Should I store references in classes in c++?
yes, why not. This question is IMO 'primarily opinion-based', so my answer is based on my own experience.
I use member references when I dont need what pointers have to offer, this way I limit possiblity that my class will be wrongly used. This means among other possibility to bind new value, assign nullptr, you cant take pointer to reference, you cannot use reference arithmetics - those features are missing in references. You should also remember that reference is not an object type, this means among others that if you put a reference as struct member, then it is no longer POD - i.e. you cannot use memcpy on it.
You should also remember that for classes which have non static reference member, compiler will not generate implicit constuctors.
For me this means references as variable members are mostly usefull when class is some kind of wrapper, or a holder. Below is an example which also shows an alternative implementation using pointer member type. This alternative implementation gives you no additional benefit to the reference one, and only makes it possible to introduce Undefined Behaviour.
struct auto_set_false {
bool& var;
auto_set_false(bool& v) : var(v) {}
~auto_set_false() { var = false; }
};
struct auto_set_false_ptr {
bool* var;
auto_set_false_ptr(bool* v) : var(v) {}
~auto_set_false_ptr() { *var = false; }
};
int main()
{
// Here auto_set_false looks quite safe, asf instance will always be
// able to safely set nval to false. Its harder (but not imposible) to
// produce code that would cause Undefined Bahaviour.
bool nval = false;
auto_set_false asf(nval);
bool* nval2 = new bool(true);
auto_set_false_ptr asf2(nval2);
// lots of code etc. and somewhere in this code a statement like:
delete nval2;
// UB
}
It is generally not a good idea to store references in a class because the class cannot be default constructed, copy assigned, move assigned, and the member cannot be changed (the reference cannot be rebound).
That renders the class uncopieable. It therefore cannot be copied, moved or placed in most containers. The far more flexible and less surprising solution is to store a pointer or a std::refernce_wrapper.
IMO references works like pointers.
The only difference is in dynamic_cast: a failed cast produces a nullpointer with pointers, and results in a throw an exception with references.
References are far better than pointers because of one reason: you don't have to play with nulls.
A reference can't be null and it is a big value to not have to check for nulls.
The small difficulty is that you have to assign reference member in a constructor.
But you can definitely change it later to other non-null value pointing to an object of a class of the member or subclass of this class.
So it supports inheritance like pointers do.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
Basing on what I've gathered from compiler writers value types are much preferred to references/pointers in terms of efficiency.
This comes from a fact that a values types are easier to reason about when you don't have to care about aliasing, externally changed memory (which the pointer refers to), cost of pointer dereference, and such things. I have to say that while I understand such concerns I still have a few questions regarding specific cases.
Case #0
void foo(const float& f)
Okay, we have a reference here, but it's constant! Sure we have a constant view (ref) of it, so externally it might be change, but it could only happen in multithreaded world and I am not sure if compiler has to take it into consideration at all if there are no synchronization primitives used. Obviously if internally we used another pointer/reference to any float variable we might be at risk of modifying the f parameter. Can compiler treat this parameter as safe (assuming we don't use any ref/ptr to float internally)?
Case #1
void foo(vector<int> f)
Talking from a C++11/14 programmer perspective I know that the vector can be safely moved into the function. As we all know, internally the container holds a pointer to an array. Will the compiler treat the pointer as safe (no externally modifications) as we just got a copy of vector, so we are the only owners of it?
In other words: is a copied object treated as a safe one (because logically we make a clone of the object), or the compiler is not allowed to make such assumptions and any ptr/ref member has to be treated as potentially dangerous as the copy ctor/op might not have made a proper copy. Shouldn't the programmer be responsible for handling shared resources when copying them?
Bottomline:
Do constant pointers/references and copied complex objects are generally slower than copies of primitives, and thus should be avoided as much as possible in performance critical code; or they are only slightly less efficient and we shouldn't fret about it?
As general rules in modern C++:
For (cheap to copy) primitive types, like int, float or double, etc., if it's an input (read-only) parameter, just pass by value:
inline double Square(double x)
{
return x*x;
}
Instead, if the type of the input parameter is not cheap to copy, but it's cheap to move, e.g. std::vector, std::string, etc., consider two sub-cases:
a. If the function is just observing the value, then pass by const reference (this will prevent useless potentially expensive deep-copies):
double Average(const std::vector<double> & v)
{
.... // compute average of elements in v (which is read-only)
}
b. If the function is taking a local copy of the parameter, then pass by value, and std::move from the value (this will allow optimizations thanks to move semantics):
void Person::SetName(std::string name)
{
m_name = std::move(name);
}
(Started as a comment but it wouldn't fit.)
Case #0 has already been discussed to death, for example:
Is it counter-productive to pass primitive types by reference?
which is already a duplicate of two other questions. In particular, I find this answer a good answer to your case #0 as well. Related questions:
Is it better in C++ to pass by value or pass by constant reference?
Reasons to not pass simple types by reference?
is there any specific case where pass-by-value is preferred over pass-by-const-reference in C++?
"const T &arg" vs. "T arg"
How to pass objects to functions in C++?
Case #1 is unclear to me: Do you need a copy or do you want to move? There is an enormous difference between the two and it is unclear from what you write which one you need.
If a reference suffices but you do a copy instead, you are wasting resources.
If you need to make a deep copy then that's all there is to it, neither references nor moving will help.
Please read this answer and revise case #1.
Case #0
No - It may be externally modified:
void foo(const float& f) {
..use f..
..call a function which is not pure..
..use (and reload) f..
}
Case #1 ... Will the compiler treat the pointer as safe (no externally modifications) as we just got a copy of vector, so we are the only owners of it?
No - it must be pessimistic. It could be taught to rely on an implementation but in general, it has no reasonable way of tracking that pointer through all possible construction scenarios for arbitrary construction to verify it is safe, even if the implementations were visible.
Bottomline:
Cost of allocation and copying containers tend to be much greater than the cost of the loads and stores -- depends on your program, hardware, and implementation!
Passing small objects and builtins by reference doesn't mean an optimizer must treat it as a reference when the implementation is visible. E.g. If it sees the caller is passing a constant, it has the liberty to make the optimization based on the known constant value. Conversely, creating a copy can interfere with the ability to optimize your program since complexity can increase. Fretting over whether or not to pass this trivial/small type by value is an old micro-optimization. Copying a (non-SSO) string or vector OTOH can be huge in comparison. Focus on the semantics first.
I write tons of performance critical code and pass almost everything by (appropriately const-qualified) reference -- including builtins. You're counting instructions and speed of memory at that point (for your parameters), which is very low in desktop and portable computers. I did plenty of testing on desktops and notebooks before settling on that. I do that for uniformness - you don't need to worry about the cost of introducing the reference (where overhead exists) outside embedded. Again, the cost to make unnecessary copies and any necessary dynamic allocations tend to be far greater. Also consider that objects have additional construction, copy, and destruction functions to execute -- even innocent looking types can cost much more to copy than to reference.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I have joined a new group that has coding guidelines that (to me) seem dated.
But just rallying against the machine without valid backup is not going to get me anywhere.
So I am turning to SO to see if we can up with rational reasons for/against (hey I may be wrong in my option so both sides of the argument would be appreciated).
The guideline that is up for argument is:
Tip: Use pointers instead of references for return arguments.
void Func1( CFoo &Return ); // bad
void Func2( CFoo *pReturn ); // good
Justification:
When you use a reference, it looks the same as a value. The caller may be surprised that his value has been changed after calling the function. The callee may innocently modify the value without meaning to affect the caller's value. By using a pointer, it is clear to both the caller and callee that the value can be changed. Using references can be particularly misleading in code reviews.
When you use a reference, it looks the same as a value.
Only if you really aren't paying attention to what you are doing. Ok, sometimes that happens, but really... no amount of coding standards can correct for people not paying attention or not knowing what they are doing.
The caller may be surprised that his value has been changed after calling the function.
If you are surprised by what happens when you call a function, then the function is poorly documented.
Given a function's name, its parameter list, and perhaps some very brief and descriptive documentation, it should be eminently clear what the function does and what its observable side effects are (including whether any arguments are modified).
The callee may innocently modify the value without meaning to affect the caller's value.
If the function is const correct, then this isn't a problem. If the function isn't const correct, then it should be made const correct, if you can (retroactively making code const correct can be an absolute beating).
This rationale doesn't make much sense, though: when you are actually writing the code for a function, you should be able to see the declarations of the parameters. If the function is so long that you can't, it's time for refactoring.
By using a pointer, it is clear to both the caller and callee that the value can be changed.
This is not entirely correct. A function can take a pointer to const object, in which case the object cannot be changed.
Using references can be particularly misleading in code reviews.
Only if the people doing the code reviews don't know what they are doing.
All of that is well and good, but why should pass-by-reference be used instead of pass-by-pointer? The most obvious reason is that a reference cannot be null.
In a function that takes a pointer, you have to check that the pointer is not null before you use it, at least with a debug assertion. During a proper code review you have to analyze more code to be sure that you don't accidentally pass a null pointer to a function that doesn't expect one. I've found that it takes much longer to review functions that take pointer arguments for this very reason; it's so much easier to get it wrong when using pointers.
It seems to me that the proper use of const would (mostly) eliminate the need for that tip. The part that still seems useful is when reading caller code, seeing:
Func1(x);
it isn't quite clear what is being done with x (particularly with a nondescript name like Func1). Instead using:
Func2(&x);
with the above convention, indicates to the caller that they should expect x to be modified.
If you have not already, buy a copy of Herb Sutter and Andrei Alexandrescu's "C++ Coding Standards: 101 Rules, Guidelines and Best Practices." Read it. Recommend it to your co-workers. It's a good base for a local coding style.
In Rule 25, the authors recommend:
"Prefer passing by reference if the argument is required and the function won't store a pointer to it or otherwise affect its ownership. This states that the argument is required and makes the caller responsible for providing a valid object."
"Argument is required" means NULL is not a valid value.
One of the most frequent causes of defects is accidental de-referencing of null pointers. Using references instead of pointers in these cases can eliminate these at compile-time.
So you have a trade-off -- eliminate a frequent source of errors, or ensure understandability of calling code by means other than the function name. I personally lean toward eliminating risk.
While I wouldn't use the tip's advice myself, the justification is valid, which is why languages like C# introduced the out and ref keywords for use at the call site.
The best argument I can come up for against it is this: instead of requiring people to use pointers, you should instead require that people write function names that reflect what the function does. When I call std::swap, I know it's going to change the value of the arguments because the name implies that. On the other hand, if I were to call a function getSize, I wouldn't expect that to modify any arguments.
Coding standards are based on habits as much as common sense. Some of your coworkers may rely on years of ingrained assumptions that a parameter not passed by pointer won't change - have pity on them.
The important part of coding standards is not that they're optimal, but that they're adhered to by everybody so that there's some consistency to the body of code.
If they really want explicit mention of out parameters at the call site, they should actually require that instead of hacking around it by trying to make pointers mean something they don't. Pointers don't imply modification any more than references do, and it's not uncommon to pass pointers for non-modified objects.
One potential way to express out parameters explicitly:
template<class T>
struct Out {
explicit Out(T& obj) : base(obj) {}
T& operator*() { return base; }
T* operator->() { return &base; }
private:
T& base;
};
template<class T>
Out<T> out(T& obj) {
return Out<T>(obj);
}
void f(Out<int> n) {
++*n;
}
int main() {
int n = 3;
f(out(n));
cout << n << '\n';
}
And as a temporary measure until they change old code to this, you can make the Out convertible to a pointer and/or reference:
// in class definition
operator T*() { return &base; }
operator T&() { return base; }
// elsewhere
void old(int *p);
void g() {
int n;
old(out(n));
}
I went ahead and wrote the various classes required for this, and for in-out parameters, in a way that should degrade nicely. I doubt I'll be using that convention any time soon (in C++, at least), but it'll work for anyone that wants to make call sites explicit.
I found there are two schools of though about this:
(a) use a pointer to show a parameter may be modified
(b) use a pointer if and only if the parameter may be null.
I agree with your motivation for (a): when reading code, you can't know all declarations, even if a mouseover gives you the declaration of the function. Mousing over hundreds of functions in thousands of lines just takes time.
I certainly see a problem here if you mix in and out parameters:
bool GetNext(int index, Type & result);
A call to this fuinction would look like this:
int index = 3;
Type t;
if (!GetNext(index, t))
throw "Damn!";
In that example, the call itself is fairly obvious, to potentially modify t. But what about index? Maybe GetNext increments the index, so you always get the next item, without the callee needing to keep caller state?
Which usually raises the reply Then the method should be GetNextAndIncrementIndex, or you should use an iterator anyway. I bet these people never had to debug code written by electrical engineers that still think Numerical Recipes is the Holy Grail of programming.
Howver I still tend to (b): simply because the problem can be avoided for new code being written, and "may be null or not" is usually the more common problem.
The justification is logically true.
It may surprise coders that the value has changed (because they thought the value was being passed by value).
But does logically true provide any meaning in this context.
So the value may change. How does this affect the correctness of the code?
Apart from it may print out a different value then an illogical human expects, but the code is doing what it is supposed to be doing and the compiler is enforcing constraints.
i recommend:
pass by reference (do not pass by pointer)
pass by const reference wherever possible (assuming you've used const correctly throughout your codebase)
place arguments/parameters which mutate at the beginning of the list
label the function appropriately
label the argument appropriately (and create methods/functions with detailed and descriptive names and few arguments)
document the result
if multiple arguments/parameters mutate, consider creating a simple class which holds these arguments (even if by reference themselves)
if they still can't function (sic) without visual and documented cues, create a lightweight template container object for the parameter which mutates, which is then passed to the method or function
I would disagree with this guideline. The confusion mentioned in the justification can be easily resolved by making sure the code is const-correct. If you are passing an input parameter to a function by reference, then it should be a const reference. If the reference is not const, that is an indication that it is an output parameter, whose value may be changed by the function.
Furthermore, when you pass a pointer to a function, rather than a reference, that instantly raises a question about whether or not this is a pointer to dynamically allocated memory, and whether or not it should be freed. Using a reference removes the temptation to call delete.
There are times when passing a pointer is appropriate, such as when it actually is a pointer to a dynamically allocated object or array, or when it makes sense for it to be null. Although, you should prefer a smart pointer in such cases. In all other cases a reference is better, IMHO.