Skip to content

Design note: Postfix operators

Herb Sutter edited this page Oct 10, 2022 · 18 revisions

Q: Why are most Cpp2 operators postfix?

A: Because of the stake in the ground of "declaration follows use."

Consider this easy case:

f: (i:int) -> string = {/*...*/}

What is the type of f? of f(42)?

  • f is a function that takes an int and returns a string.
  • f(42) is a string.

That was a good warmup. Now consider this function:

f: (i:int) -> * (:int) -> string = {/*...*/}

This reads left to right:

  • f is type (int) -> * (int) -> string, a function that takes an int and returns a pointer to a function that takes an int and returns a string. And we just said exactly that in code.
  • f(42) is type * (int) -> string, a pointer to a function that takes an int and returns a string.
  • f(42)* is type (int) -> string, a function that takes an int and returns a string.
  • f(42)*(1) is type string, a string.

image

Similarly, consider:

x: * int = /*...*/;

This also reads left to right:

  • x is type *int, a pointer to an int.
  • x* is type int, an int.

image

In Cpp2, my current experiment to disambiguate postfix unary operators that look similar to binary operators is that the postfix unary operators have to be written without intervening whitespace, such as x*. And that's pretty much the only rule to remember... additionally, as a convenience, cases like x**y mean the same as x* * y since they can't mean anything else, which is convenient so we can still write things like a*b and a*2 conveniently with the obvious meaning. That's the current experiment!

Note: This is the writeup that I promised in the CppCon 2022 talk.