On Wed, Jan 26, 2022 at 3:03 PM Victor Eijkhout <eijkhout@tacc.utexas.edu> wrote:
On , 2022Jan26, at 13:33, Arthur O'Dwyer <arthur.j.odwyer@gmail.com> wrote:

FWIW, I teach
- pass-by-value
- problems: performance of all those copies; how to write an out-parameter?
- C solution: pointers (a pointer holds a memory address)
- pass-by-pointer
- C++ enhancement: references (***)
- pass-by-reference

I agree with your sequence, and I use something very similar.


Why do you teach the “pass by pointer”? Maybe you and I have different audiences.

I'm sure we do. :) At least three reasons come to mind:
- My target audience is usually new-hire software engineers, who will have taken C in college and so they already know pointers are coming; they probably think they're scary and confusing (because their college professor did a terrible job explaining them); there's no point trying to hide the fact that C++ has pointers because the students are already aware of it. Give them a quick glimpse of the monster in the first five minutes. :)
- Pass-by-pointer solves the technical machine-level problem of "how do I pass a Widget efficiently without copying?" You just pass its address; technical problem solved! The only problem with pointers is that they look ugly. So we immediately present C++'s solution to the looking ugly problem, which is to use references instead of pointers. But references aren't magic, and they aren't inscrutable; they behave exactly like pointers. Usually I'll fire up Godbolt at this point to show that we get the exact same x86-64 machine instructions for `int ptr(int *p) { return *p; }` and `int ref(int& r) { return r; }`. References behave like pointers, but look like values; and this is why they're so awesome. They let us write code that looks like it's taking a string by value, but behaves as efficiently as if it were taking just a pointer. (We have already introduced the phrase "zero-overhead abstraction" by this point.)
- Most industry codebases pass out-parameters by pointer as a matter of style. This is a fine place to talk about that, since they'll be expected to do it on the job.

I don't bring pointers back into the discussion until `new` and `delete` (which again are relatively quickly wrapped up in C++ clothing via `unique_ptr`, although not right away, because we have to get there via move semantics, and move semantics are justified by pilfering/taking-ownership, which is justified by talking about responsibilities of ownership, and the primary interesting responsibility to talk about is the responsibility to call `delete`).

> Agree with describing a reference as “a new name”. I usually call it an alias. (And I stress how it is not a pointer, for the students that have learned C)

I reserve "alias" for its technical meaning — type aliases, a.k.a. typedefs. I call references references. But I do implicitly analogize them to names, in that I talk about how when we assign `i = 1;` we're (of course) not actually assigning to the letter `i`; we're assigning to the object referred to by the name `i`. And when we assign `r = 1;`, we're not assigning anything to `r` itself; we're assigning to the object referred to by `r`.

> As with universal references become state of the art for ordinary programmers with C++20, I wonder whether we should now teach them early or even first.

Hard no. Forwarding references are not ordinary-programmer material, nor should they ever be. (Ranges relies heavily on forwarding references, but essentially nothing in the average programmer's life is a Ranges-style range.)
Tangentially related: I have recently run into multiple intermediate-level C++ programmers who knew just enough about value categories to be surprised to find that `const T&` will bind to an rvalue argument — they were, like, honestly trying to puzzle out the right way to refactor `void f(const string&)` so that it would accept rvalue strings! (It already does accept them, of course.)
I've quipped that `const T&` is "the O.G. universal reference," in that it has bound to both lvalues and rvalues since C++98.