If you were to look at this code,
int x = 0; function(x); std::cout << x << '\n';
you would not be able to verify through any means of syntax, that parameter x is being passed by reference or that it's being passed by value. The only way you would know for sure, is if you looked at either the function declaration or function definition.
Here is a simple example of how I believe this could be a problem:
std::string Lowercase(std::string str); //<- this is hidden away in code; probably in a different file. int main(){ std::string str = "HELLO"; Lowercase(str); std::cout << str << '\n'; //<- Bug! we expected to output "hello". The problem is not very easy to spot, especially when a function name sounds as though it will change the passed in value. }
In order to avoid having to jump between the function call and the function declaration (or in some cases, the documentation) in order to understand the function behavior, is there a way to explicitly document in the syntax of a function call that the parameter is expected to change (i.e. a reference parameter) or that a copy is being sent (i.e. pass by value)?
I realize that there is also the option of passing by const& which has the similar notion to passing by value, in that the variable passed in, will not have its value changed after the function call.
I'm sure there are all kinds of situations in the language that might add to the complexity of understanding how a parameter is being passed- but I'm curious, is there a way to combat this problem in the way I want to?
I've noticed that some people write two similar functions. One of them takes a value parameter, the other one takes a pointer. That allows calling a function like this:
Lowercase(str); //we assume the value will not change Lowercase(&str); //we assume the value will change
But this solution has many other issues, and I would not like to lose the benefit of references. Plus, we are still making assumptions on the behavior.
To pass a value by reference, argument pointers are passed to the functions just like any other value. So accordingly you need to declare the function parameters as pointer types as in the following function swap(), which exchanges the values of the two integer variables pointed to, by their arguments.
"Passing by value" means that you pass the actual value of the variable into the function. So, in your example, it would pass the value 9. "Passing by reference" means that you pass the variable itself into the function (not just the value). So, in your example, it would pass an integer object with the value of 9.
They are synonymous. The term call-by-value means exactly the same as pass-by-value. However, I prefer the pass-by-value form, as it's the parameter that is passed that it refers to. A call can have parameters that are passed by value as well as parameters passed by reference.
Pass by reference is something that C++ developers use to allow a function to modify a variable without having to create a copy of it. To pass a variable by reference, we have to declare function parameters as references and not normal variables.
Some people insist that the correct way to pass mutable object is to use a pointer. That is, you would pass
Lowercase(&str);
... and Lowercase()
would, obviously, be implemented to take a pointer. That approach may suit your needs.
I want to mention, however, that this is not what I would do! Instead, the approach I favor is to use appropriate names instead. For example,
inplace_lowercase(str);
pretty much says what it is going to do. Clearly, inplace_lowercase()
would actually be an algorithm and with a bit of magic could be reasonably be called as
inplace_lowercase(str.begin() + 1, str.end());
as well.
Here are a few reasons why I don't like passing arguments by pointer and/or why I don't believe in an explicit indication of how the argument is passed:
T const*
.I'm not sure I understand your requirements completely, but maybe this is something you can use:
template<typename T> void foo( T ) { static_assert( sizeof(T)==0, "foo() requires a std::ref" ); } void foo( std::reference_wrapper<int> t ) { // modify i here via t.get() or other means of std::reference_wrapper } int main() { int i = 42; // foo( i ); // does not compile, static_assert fires foo( std::ref( i ) ); // explicit std::ref visible on the caller's side }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With