Reputation: 1903
Q: Is pass-by-value/reference defined strictly by behavior or implementation wise in C++, and can you provide an authoritative citation?
I had a conversion with a friend about pass-by-value/reference in C++. We came to a disagreement on the definition of pass-by-value/reference. I understand that passing a pointer to a function is still pass-by-value since the value of the pointer is copied, and this copy is used in the function. Subsequently, dereferencing the pointer in the function and mutating it will modify the original variable. This is where the disagreement appears.
His stance: Just because a pointer value was copied and passed to the function, performing operations on the dereferenced pointer has the ability to affect the original variable, so it has the behavior of pass-by-reference, passing a pointer to a function.
My stance: Passing a pointer to a function does copy the value of the pointer, and operations in the function may affect the original variable; however, just because it may affect the original, this behavior does not constitute it to be pass-by-reference since it is the implementation of the language that is what defines these terms, pass-by-value/reference.
Quoting from the definition given by the highest voted answer here: Language Agnostic
Pass by Reference
When a parameter is passed by reference, the caller and the callee use the same variable for the parameter. If the callee modifies the parameter variable, the effect is visible to the caller's variable.
Pass by Value
When a parameter is passed by value, the caller and callee have two independent variables with the same value. If the callee modifies the parameter variable, the effect is not visible to the caller.
I still have an ambiguous feeling after reading these. For example, the pass by value/reference quotes can support either of our claims. Can anyone clear up the definitions of whether these definition stem from behavior or implementation and provide a citation? Thanks!
Edit: I should be a little more careful of my vocabulary. Let me extend my question with a clarification. What I mean when questioning pass-by-reference is not talking purely about the C++ implementation of & reference, but instead also the theory. In C++, is it that the & pass-by-reference is true PBR because not only can it modify the original value, but also the memory address of the value. This leads to this, example with pointers also count as PBR?
void foo(int ** bar){
*bar = *bar+(sizeof(int*));
cout<<"Inside:"<<*bar<<endl;
}
int main(){
int a = 42;
int* ptrA = &a;
cout<<"Before"<<ptrA<<endl;
foo(&ptrA);
cout<<"After:"<<ptrA<<endl;
}
The output would be that After ptrA
is equal to Inside
, meaning that not only can the function modify a
, but ptrA
. Because of this, does this define call-by-reference as a theory: being able to not only modify the value, but the memory address of the value. Sorry for the convoluted example.
Upvotes: 2
Views: 346
Reputation: 40852
Those terms are about the variable that is passed, in this case the pointer. If you pass a pointer to a function then the variable that is passed is the pointer - holding the address of the object - to an object and not the object it points to.
If you pass a pointer by value then chaning the object it is pointing to in the function would not affect the pointer that was passed to the function.
If you pass the pointer by reference then you can change in the function where the pointer is pointing to and it would modifiy the pointer that was passed to this function.
Thats how it is defined. Otherwise you could argue that if you have a global std::map<int,SomeObject>
and you pass an int
as key to the object, would also be a pass by reference because you can modify the objects in that global map, and the caller would see those changes. Because this int
is also just a pointer to an object.
Upvotes: 1
Reputation: 41780
You talk a lot about pointers here, which they are indeed passed by value most of the time, but you don't mention actual C++ references, which are actual references.
int a{};
int& b = a;
// Prints true
std::cout << std::boolalpha << (&b == &a) << std::endl;
Here, as you can see, both variables have the same address. Put it simply, especially in this case, references act as being another name for a variable.
References in C++ are special. They are not objects, unlike pointers. You cannot have an array of references, because it would require that references has a size. Reference are not required to have a storage at all.
What about actually passing a variable by reference then?
Take a look at this code:
void foo(int& i) {
i++;
}
int main() {
int i{};
foo(i);
// prints 1
std::cout << i << std::endl;
}
In that particular case, the compiler must have a way to send to which variable the reference is bound. Indeed references are not required to have any storage, but they are not required to not have one either. In this case, if optimizations are disabled, it is most likely that the compiler implements the behavior of references using pointers.
Of course, if optimizations are enabled, it may skip the passing and completely inline the function. In that case, the reference don't exist, or don't have any storage, because the original variable will be used directly.
Other similar optimization happens with pointers too, but that's not the point: The point is, the way references are implemented is implementation defined. They are most likely implemented in term of pointers, but they are not forced to, and the way a reference is implemented may vary from case to case. The behavior of references are defined by the standard, and really is pass-by-reference.
What about pointers? Do they count as passing by reference?
I would say no. Pointers are objects, just like int
, or std::string
. You can even pass a reference to a pointer, allowing you to change the original pointer.
However, pointers do have reference semantics. They are not reference indeed, just like std::reference_wrapper
is not a reference either, but they have reference semantics. I wouldn't call passing a pointer "passing by reference", because you don't have an actual reference, but you indeed have reference semantics.
A lot of things have reference semantics, pointers, std::reference_wrapper
, a handle to a resource, even GLuint
, which are handle to an opengl object, all have reference semantics, but they are not references. You don't have a reference to the actual object, but you can change the pointed-to object through these handles.
There are other good articles and answers you can read about. They are all very informative about value and reference semantics.
Upvotes: 3
Reputation: 1577
Passing by value/reference (you forgot one which is passing the address to the location in memory by using a pointer) is part of the implementation of C++.
There is one more way to pass variables to functions, and that is by address. Passing an argument by address involves passing the address of the argument variable (using a pointer) rather than the argument variable itself. Because the argument is an address, the function parameter must be a pointer. The function can then dereference the pointer to access or change the value being pointed to.
Take a look here at what I have always thought to be an authoritative Source: Passing Arguments by Address.
You're correct in regards to a value being copied when passing by value. This is the default behavior in C++. The advantage of passing by value into a function is that the original value cannot be changed by the function when the value is passed into it and this prevents any unwanted bugs and/or side effects when changing the value of an argument.
The problem with passing by Value is that you will incur a huge performance penalty if you pass an entire struct or class many times into your function as you will be passing entire copies of the value you are trying to pass AND in the case of a mutator method in a class, you will not be able to change the original values and will therefore end up creating multiple copies of the data you are trying to modify because you will be forced to return the new value from the function itself instead of from the location in memory where the data structure resides. This is just completely inefficient.
You only want to pass by value when you don't have to change the value of the argument.
Here is a good source on the topic of Passing Arguments by Value.
Now, you will want to use the "Pass by Reference" behavior when you do need to change the value of an argument in the case of arrays, Classes, or structs. It is more efficient to change the value of a data structure by Passing a Reference to the location in memory where the data structure resides into the function. This has the benefit that you will not have to return the new value from the function but rather, the function can then change the value of the reference you have given it directly where it resides in memory.
Take a look here to read more about about Passing an Argument by Reference.
EDIT: In regards to the issue as to whether or not you are passing a non-const by reference or by value when using a pointer, it seems to me the answer is clear. When using a pointer to a non-const, it is neither. When passing a pointer as an argument to a function, you in fact are "Passing the Value" of the ADDRESS into the function and since it is a copy of the ADDRESS of the location in memory where the non-const resides, then you are able to change the Value of the data at that location and not the value of the pointer itself. If you do not want to change the value of the data located at the address pointed to by the pointer being passed by value as an argument into your function, it is good form to make the pointer to an argument a const since the function will not be changing the value of the data itself.
Hope that makes sense.
Upvotes: 2
Reputation: 329
References are different from pointers. The main reason references were introduced is to support Operator Overloading. C++ is derived from C and during the process, Pointers were inherited from C. As Stroustrup says: C++ inherited pointers from C, so I couldn't remove them without causing serious compatibility problems.
So, effectively there are three different ways of parameters passing:
Now, pass by pointer has the same effect as pass by reference. So how to decide on what you want to use? Going back to what Stroustrup said:
That depends on what you are trying to achieve: If you want to change the object passed, call by reference or use a pointer; e.g. void f(X&); or void f(X*); If you don't want to change the object passed and it is big, call by const reference; e.g. void f(const X&); Otherwise, call by value; e.g. void f(X);
Ref: http://www.stroustrup.com/bs_faq2.html#pointers-and-references
Upvotes: 1