2
#include <iostream>
using namespace std;

template <class T>
void swap1(T& a, T& b) {
    T c = a;
    a = b;
    b = c;
}

int main() {
    int n1 = 5;
    int n2 = 7;
    swap1(n1, n2);
    cout << n1 << " " << n2 << endl;

    int *p1 = &n1;
    int *p2 = &n2;
    swap1(*p1, *p2);
    cout << n1 << " "<< n2 << endl;
}

In many languages, when you call a function, you first evaluate its arguments, and then apply the function to the results of the evaluation.

If we follow this rule here however, swap1(n1, n2) and swap1(*p1, *p2) would both evaluate to swap1(5, 7) which makes no sense.

So, what are the rules of evaluation in this case? Also, what are the rules of evaluating a function in general in C++?

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
user52874
  • 131
  • 3
  • Inside the function, the arguments `a` and `b` ***references*** the *variables* `n1` and `n2` from the `main` function. You don't pass in the *values* of those variables. It doesn't matter that you use dereferenced pointers, since those pointers point to `n1` and `n2`. – Some programmer dude Oct 16 '17 at 07:36
  • Consider operation like `int n = 2; n++;` Are you going to insist it's equivalent to `2++`...? Hint: study differences between l-values and r-values, known since beginnings of C. – CiaPan Oct 16 '17 at 07:44
  • 1
    Evaluating an lvalue does NOT mean to access the stored value; the result of evaluating `*p1` is a designator of that memory location, not `5`. Then when the function parameters are initialized: the effect of initializing a reference parameter with an lvalue expression, is to bind the reference to the expression (again, does not access the memory location) – M.M Oct 16 '17 at 07:45
  • I've always thought that n++ was just syntax sugar for n = n + 1 but I guess that's irrelevant to this? – user52874 Oct 16 '17 at 07:48
  • 1
    @user52874 It's most definitely not syntactic sugar for that. The return value differs. The number of times `n` is evaluated differs. The `operator` function used when overloading the operation differs. – Angew is no longer proud of SO Oct 16 '17 at 07:54
  • Very nice question, shows actual thought about the rules in play. – Angew is no longer proud of SO Oct 16 '17 at 07:56
  • @M.M "the result of evaluating *p1 is a designator of that memory location". What is a designator? Is it the memory address? – user52874 Oct 19 '17 at 01:28
  • @user52874 no, it's not an address. It's an abstract thing that means the memory location. – M.M Oct 19 '17 at 01:35
  • @M.M So the behavior this abstract thing depends on what you do with it? If you say `cout << *p1`, then its content is printed out. If you say `*p1 = 0`, then you set the content of this abstract thing to the value 0? Also, is a designator the same as an object in C (region of data storage in the execution environment, the contents of which can represent values)? https://stackoverflow.com/questions/10579825/objects-in-c-language – user52874 Oct 19 '17 at 02:07
  • The designator is not the object. It's an expression designating the object. `*p1 = 0;` modifies the object. The designator lets us know which object to modify. – M.M Oct 19 '17 at 02:18
  • @M.M is there anywhere I can read more about designators? I can't seem to find anything about it on Google. – user52874 Oct 19 '17 at 07:32
  • @user52874 See C++14 [basic.lval]/1.1 – M.M Oct 19 '17 at 07:50

1 Answers1

2

In many languages, when you call a function, you first evaluate its arguments, and then apply the function to the results of the evaluation.

That is exactly what happens in C++ too. However, we have to analyse what "evaluating a function's arguments" actually means. In C++, it means "copy-initialising the parameter using the expression specified as the argument." The way to copy-initialise a C++ reference is to bind to the entity desginated by the initialiser. In the case of lvalue references (like in your swap), this means the initialiser must be an lvalue. n1 is an lvalue, and so is *p1. Since the reference wants an lvalue, that's all the evaluation there is to do.

If the parameter was of value type (not a reference), the lvalue-to-rvalue conversion would be applied next to initialise the value. But since we're initialising a reference, this conversion does not happen.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455