2

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.

Miket25
  • 1,895
  • 3
  • 15
  • 29
  • 2
    C has no pass by reference, so it "simulates' it through passing and dereferencing a pointer. In C++ there is a real pass by reference. –  Jul 25 '17 at 15:35
  • imho you can call it either way. A pointer is passed-by-value, but as a pointer is a reference to the pointee (in the general sense of "reference") one could also say that passing a pointer is passing the pointee by reference. – 463035818_is_not_an_ai Jul 25 '17 at 15:36
  • @manni66 Thanks, but I'm already aware of that. My concern is whether the definition stems from behavior or implementation. – Miket25 Jul 25 '17 at 15:38
  • @tobi303, can you please provide a citation of your statement that's authoritative? – Miket25 Jul 25 '17 at 15:38
  • IMO They're just terms to describe and understand the behaviour of certain operations, there wouldn't be and neither need there be a clear-cut definition – Passer By Jul 25 '17 at 15:38
  • A pointer is both a value and a reference. Consider a hypothetical `void allocate_here(int ** allocation, int * size) { *size = /*something*/; *allocation = new int[*size]; }` – Caleth Jul 25 '17 at 15:52
  • I'm not sure I understand the question. In the computer science sense of the word, C pointers are a kind of reference, so passing a pointer/memory address can be thought of as a kind of "pass-by-reference" if you like. In C++ we use the term "reference" more narrowly, and pass-by-pointer is semantically different from pass-by-C++-reference (although the implementation may well end up being the same). – Tristan Brindle Jul 25 '17 at 15:55
  • 3
    This is basically asking us for opinions on a philosophical issue of semantics and whether scenarios can be described by more than one label (hint: yes). I don't think that's very useful. For example, none of the answers seem to have directly addressed what you seem to be asking. – underscore_d Jul 25 '17 at 16:02
  • The C++ standard defines what reference types are. When you pass a reference type, you pass "by reference". Otherwise you pass by value. There really isn't that much to discuss. Passing a pointer by value is passing by value. – juanchopanza Jul 25 '17 at 16:12
  • @juanchopanza Yes, and that explanation is very well-defined in the Standard. In that sense, there's nothing to debate. It's only if people start getting philosophical and using flexible definitions that debate becomes possible, and I doubt SO is the place for that side of the discussion. – underscore_d Jul 25 '17 at 16:33
  • @underscore_d please see my edit. I agree, this question is sounding more philosophical, which is not my intention. I just desire to know where these definitions bound to. – Miket25 Jul 25 '17 at 16:38
  • @Miket25 You're making the question more opinion based. There are clear definitions for this stuff in the C++ language spec. But some people use to use their own instead. – juanchopanza Jul 25 '17 at 16:41
  • @Miket25 The edit changes nothing. You just added another, obfuscating level of indirection: now you're passing a pointer-to-a-pointer by value, letting you dereference it & so modify either the pointed pointer or what _it_ points to. The pointer-to-pointer is still passed by value. Make it a pointer-to-pointer-to-pointer: nothing changes! It's always passed by value. If that value is an address of a modifiable object, again there is some philosophical wiggle-room to say that ultimately you _might as well be_ passing by reference... but you're literally not; you're passing an address by value. – underscore_d Jul 25 '17 at 18:40

4 Answers4

3

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.

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
  • How does this address the grey area that seems to be the focus of the question: whether passing a pointer to a non-`const` object qualifies as passing by value, by reference, or both? – underscore_d Jul 25 '17 at 16:09
  • Your addition at the end really clears up my confusion! I would happily accept this answer if you can provide a citation for your end. – Miket25 Jul 25 '17 at 16:47
  • @Miket25 I added some links. I think they explain pretty well what is value and reference semantics, as well as differences between pointers and references in C++. Tell me if there anything else. – Guillaume Racicot Jul 25 '17 at 16:58
2

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.

lopezdp
  • 1,538
  • 3
  • 21
  • 38
  • How does this address the grey area that seems to be the focus of the question: whether passing a pointer to a non-`const` object qualifies as passing by value, by reference, or both? – underscore_d Jul 25 '17 at 16:09
  • 1
    underscore_d, check out my edit and let me know if that makes sense to you. I think OP is not taking into account that when passing a pointer into a function you are in fact passing by address which is passing the value of the address and not the value of the data... – lopezdp Jul 25 '17 at 16:26
1

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:

  • Pass by value
  • Pass by reference &
  • Pass by pointers.

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

Enigma
  • 329
  • 1
  • 10
  • Imho deciding between pass by value and pass by reference is the easy decision, the one that isnt always so clear is whether to use a pointer or a reference which has quite different implications, but the quote just says "either one or the other" – 463035818_is_not_an_ai Jul 25 '17 at 16:09
1

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.

t.niese
  • 39,256
  • 9
  • 74
  • 101