// parameter z is a std::map
)
= {
// ...
}
```
The parameter type can be deduced by writing `_` (the default, so it can be omitted). You can use `is` to declare a type constraint (e.g., a concept) that a deduced type must match, in which case `_` is required. For example:
``` cpp title="Declaring a parameter of constrained deduced type" hl_lines="2 3 6"
// ordinary generic function, x's type is deduced
print: (x: _) = { std::cout << x; }
print: (x) = { std::cout << x; } // same, using the _ default
// number's type is deduced, but must match the std::integral concept
calc: (number: _ is std::integral) = { /*...*/ }
```
There are six ways to pass parameters that cover all use cases, that can be written before the parameter name:
| Parameter ***kind*** | "Pass an `x` the function ______" | Accepts arguments that are | Special semantics | ***kind*** `x: X` compiles to Cpp1 as |
|---|---|---|---|---|
| **`in`** (default) | can read from | anything | always `#!cpp const`automatically passes by value if cheaply copyable
to guarantee a by-reference passing, use `in_ref` | `X const x` or
`X const& x` |
| **`copy`** | gets a copy of | anything | acts like a normal local variable initialized with the argument | `X x` |
| **`inout`** | can read from and write to | lvalues | | `X& x` |
| **`out`** | writes to (including construct) | lvalues (including uninitialized) | must `=` assign/construct before other uses | `cpp2::impl::out` |
| **`move`** | moves from (consume the value of) | rvalues | automatically moves from every definite last use | `X&&` |
| **`forward`** | forwards | anything | automatically forwards from every definite last use | `auto&&`, and if a specific type is named also a `requires`-constraint requiring convertibilty to that type |
> Note: All parameters and other objects in Cpp2 are `#!cpp const` by default, except for local variables. For details, see [Design note: `#!cpp const` objects by default](https://github.com/hsutter/cppfront/wiki/Design-note%3A-const-objects-by-default).
For example:
``` cpp title="Declaring parameter kinds" hl_lines="2 3 10"
append_x_to_y: (
x : i32, // an i32 I can read from (i.e., const)
inout y : std::string // a string I can read from and write to
)
= {
y = y + to_string(x); // read x, read and write y
}
wrap_f: (
forward x // a generic value of deduced type I can forward
) // (omitting x's type means the same as ': _')
= {
global_counter += x; // ok to read x
f(x); // last use: automatically does 'std::forward(x)'
}
```
### Return values
A function can return either a single anonymous return value, or a return parameter list containing named return value(s). The default is `#!cpp -> void`.
#### Single anonymous return values
**`#!cpp ->` _kind_ `X`** to return a single unnamed value of type `X` using the same kinds as in the [parameters](#parameters) syntax, but where the only legal kinds are `move` (the default) or `forward` (with optional `forward_ref`; see below). The type can be `#!cpp -> void` to signify the function has no return value. If `X` is not `#!cpp void`, the function body must have a `#!cpp return /*value*/;` statement that returns a value of type `X` on every path that exits the function, or must be a single expression of type `X`.
To deduce the return type, write `_`:
- `-> _` deduces by-value return.
- `-> forward _` deduces by-value return (if the function returns a prvalue or type member object) or by-reference return (everything else), based on the `decltype` of the returned expression.
- `-> forward_ref _` deduces by-reference return only.
A function whose body is a single expression `= expr;` defaults to `-> forward _ = { return expr; }`.
For example:
``` cpp title="Functions with an unnamed return value" hl_lines="2 4 7 9 12 14 15 18 20 22"
// A function returning no value (void)
increment_in_place: (inout a: i32) -> void = { a++; }
// Or, using syntactic defaults, the following has identical meaning:
increment_in_place: (inout a: i32) = { a++; }
// A function returning a single value of type i32
add_one: (a: i32) -> i32 = { return a+1; }
// Or, using syntactic defaults, the following has identical meaning:
add_one: (a: i32) -> i32 = a+1;
// A generic function returning a single value of deduced type
add: (a:T, b:U) -> forward _ = { return a+b; }
// Or, using syntactic defaults, the following have identical meaning:
add: (a, b) -> forward _ = a+b;
add: (a, b) a+b;
// A generic function expression returning a single value of deduced type
vec.std::ranges::sort( :(x:_, y:_) -> forward _ = { return y (x:X, y:Y) -> forward _ = { return y ( /* parameter list */ )`** to return a list of named return parameters using the same [parameters](#parameters) syntax, but where the only needed kinds are `out` (the default, which moves where possible) or `forward`. The function body must [initialize](objects.md#init) the value of each return-parameter `ret` in its body the same way as any other local variable. An explicit return statement is written just `#!cpp return;` and returns the named values; the function has an implicit `#!cpp return;` at the end. If only a single return parameter is in the list, it is emitted in the lowered Cpp1 code the same way as a single anonymous return value above, so its name is only available inside the function body.
For example:
``` cpp title="Function with multiple/named return values" hl_lines="1 3-4 7-8 14 16-17"
divide: (dividend: int, divisor: int) -> (quotient: int, remainder: int) = {
if divisor == 0 {
quotient = 0; // constructs quotient
remainder = 0; // constructs remainder
}
else {
quotient = dividend / divisor; // constructs quotient
remainder = dividend % divisor; // constructs remainder
}
}
main: () = {
div := divide(11, 5);
std::cout << "(div.quotient)$, (div.remainder)$\n";
}
// Prints:
// 2, 1
```
This next example declares a [member function](types.md#this-parameter) with multiple return values in a [type](types.md) named `set`:
``` cpp title="Member function with multiple/named return values" hl_lines="7 9 10 22-24"
set: type = {
container: std::set;
iterator : type == std::set::iterator;
// A std::set::insert-like function using named return values
// instead of just a std::pair/tuple
insert: (inout this, value: Key) -> (where: iterator, inserted: bool) = {
set_returned := container.insert(value);
where = set_returned.first;
inserted = set_returned.second;
}
ssize: (this) -> i64 = std::ssize(container);
// ...
}
use_inserted_position: (_) = { }
main: () = {
m: set = ();
ret := m.insert("xyzzy");
if ret.inserted {
use_inserted_position( ret.where );
}
assert( m.ssize() == 1 );
}
```
#### Function outputs are not implicitly discardable
A function's outputs are its return values, and the "out" state of any `out` and `inout` parameters.
Function outputs cannot be silently discarded. To explicitly discard a function output, assign it to `_`. For example:
``` cpp title="No silent discard" hl_lines="9 11 13 17-18 23-24 29-30"
f: () -> void = { }
g: () -> int = { return 10; }
h: (inout x: int) -> void = { x = 20; }
main: ()
= {
f(); // ok, no return value
std::cout << g(); // ok, use return value
_ = g(); // ok, explicitly discard return value
g(); // ERROR, return value is ignored
{
x := 0;
h( x ); // ok, x is referred to again...
std::cout << x; // ... here, so its new value is used
}
{
x := 0;
h( x ); // ok, x is referred to again...
_ = x; // ... here where its value explicitly discarded
}
{
x := 0;
h( x ); // ERROR, this is a definite last use of x
} // so x is not referred to again, and its
// 'out' value can't be implicitly discarded
}
```
> Cpp2 imbues Cpp1 code with nondiscardable semantics, while staying fully compatible as usual:
>
> - A function written in Cpp2 syntax that returns something other than `#!cpp void` is always compiled to Cpp1 with `[[nodiscard]]`.
>
> - A function call written in Cpp2 `x.f()` member call syntax always treats a non-`#!cpp void` return type as not discardable, even if the function was written in Cpp1 syntax that did not write `[[nodiscard]]`.
For details and rationale, see [Design note: Explicit discard](https://github.com/hsutter/cppfront/wiki/Design-note%3A-Explicit-discard).
### Using function types
The same function parameter/return syntax can be used as a function type, for example to instantiate `std::function` or to declare a pointer to function variable. For example:
``` cpp title="Using function types with std::function and *pfunc" hl_lines="4 7"
decorate_int: (i: i32) -> std::string = "--> (i)$ <--";
main: () = {
pf1: std::function< (i: i32) -> std::string > = decorate_int&;
std::cout << "pf1(123) returned \"(pf1(123))$\"\n";
pf2: * (i: i32) -> std::string = decorate_int&;
std::cout << "pf2(456) returned \"(pf2(456))$\"\n";
}
// Prints:
// pf1 returned "--> 123 <--"
// pf2 returned "--> 456 <--"
```
## Control flow
### `#!cpp if`, `#!cpp else` — Branches
`if` and `else` are like always in C++, except that `(` `)` parentheses around the condition are not required. Instead, `{` `}` braces around a branch body *are* required. For example:
``` cpp title="Using if and else" hl_lines="1 4"
if vec.ssize() > 100 {
do_general_algorithm( container );
}
else {
do_linear_scan( vec );
}
```
### `#!cpp for`, `#!cpp while`, `#!cpp do` — Loops
**`#!cpp do`** and **`#!cpp while`** are like always in C++, except that `(` `)` parentheses around the condition are not required. Instead, `{` `}` braces around the loop body *are* required.
**`#!cpp for range do (e)`** ***statement*** says "for each element in `range`, call it `e` and perform the statement." The loop parameter `(e)` is an ordinary parameter that can be passed using any [parameter kinds](#parameters); as always, the default is `in`, which is read-only and expresses a read-only loop. The statement is not required to be enclosed in braces.
Every loop can have a `next` clause, that is performed at the end of each loop body execution. This makes it easy to have a counter for any loop, including a range `#!cpp for` loop.
> Note: Whitespace is just a stylistic choice. This documentation's style generally puts each keyword on its own line and lines up what follows.
For example:
``` cpp title="Using loops" hl_lines="4 5 13 16 17 22-24"
words: std::vector = ("Adam", "Betty");
i := 0;
while i < words.ssize() // while this condition is true
next i++ // and increment i after each loop body is run
{ // do this loop body
std::cout << "word: (words[i])$\n";
}
// prints:
// word: Adam
// word: Betty
do { // do this loop body
std::cout << "**\n";
}
next i-- // and decrement i after each loop body is run
while i > 0; // while this condition is true
// prints:
// **
// **
for words // for each element in 'words'
next i++ // and increment i after each loop body is run
do (inout word) // declare via 'inout' the loop can change the contents
{ // do this loop body
word = "[" + word + "]";
std::cout << "counter: (i)$, word: (word)$\n";
}
// prints:
// counter: 0, word: [Adam]
// counter: 1, word: [Betty]
```
There is no special "select" or "where" to perform the loop body for only a subset of matches, because this can naturally be expressed with `if`. For example:
``` cpp title="Using loops + if" hl_lines="7"
// Continuing the previous example
i = 0;
for words
next i++
do (word)
if i % 2 == 1 // if i is odd
{ // do this loop body
std::cout << "counter: (i)$, word: (word)$\n";
}
// prints:
// counter: 1, word: [Betty]
```
Here is the equivalent of the Cpp1 code `for ( int i = 0; i < 10; ++i ){ std::cout << i; }`:
``` cpp title="Equivalent of Cpp1 'for ( int i = 0; i < 10; ++i ){ std::cout << i; }'"
(copy i := 0)
while i < 10
next i++ {
std::cout << i;
}
```
Line by line:
- `(copy i := 0)`: Any statement can have [statement-local parameters](declarations.md#from-functions-to-local-scopes-and-back-again), and this is declaring `i` as an `int` that's local to the loop. Parameters by default are `const`, and for not-cheap-to-copy types they bind to the original value; so because we want to modify `i` we say `copy` to explicitly declare this is the loop's own mutable scratch variable.
- `while i < 10`: The termination condition.
- `next i++`: The end-of-loop-iteration statement. Note `++` is always postfix in Cpp2.
#### Loop names, `#!cpp break`, and `#!cpp continue`
Loops can be named using the usual **name `:`** syntax that introduces all names, and `#!cpp break` and `#!cpp continue` can refer to those names. For example:
``` cpp title="Using named break and continue" hl_lines="1 3 6 10"
outer: while i `#!cpp throw`, `#!cpp try`, `#!cpp catch` — Exceptions
**`#!cpp throw`**, **`#!cpp try`** and **`#!cpp catch`** are like always in C++, except:
- `#!cpp throw(something)` requires parentheses around the thrown value
- `#!cpp catch` uses Cpp2's usual [parameter syntax](#parameters), so you can write `#!cpp catch(obj: type)` to catch an exception of a specific type, and `#!cpp catch(_)` to catch any thrown exception
For example:
``` cpp title="Throw, try, catch" hl_lines="1 3-5 9-11"
// This program will print "caught something else"
main: () = {
try {
throw( "xyzzy" );
}
catch( i: int ) {
std::cout << "caught int with value " << i;
}
catch( _ ) {
std::cout << "caught something else";
}
}
```
### `#!cpp static` — Global variables inside functions
**`#!cpp static`** can be specified as the first token of a local variable declaration at function scope, to denote that the variable is a global value initialized thread-safely the first time the line is executed (aka C++ "function local static," aka "magic static").
For example (see also [`@singleton`](metafunctions.md#singleton)):
``` cpp title="static variable (function local scope only)" hl_lines="15"
// For exposition only - normally, use @singleton for convenience
my_singleton: type = {
value : int = 42;
print : (this) = std::cout << "(value)$\n";
//--------------------------------------------------------------------
// writing '@singleton type' above would generate these two functions
// but this example shows them for exposition of 'static'
//
private
operator=: (out this) = { }
instance : () -> forward my_singleton = {
static _instance: my_singleton = (); // singleton variable
return _instance;
}
//--------------------------------------------------------------------
}
main: () = {
my_singleton::instance().print();
}
```
## Move/forward from definite last use
In a function body, a **definite last use** of a local name is a single use of that name in a statement that is not in a loop, where no control flow path after that statement mentions the name again.
For each definite last use:
- If the name is a `copy` or `move` parameter or is a local object whose name does not start with `guard`, we know the object will not be used again before being destroyed, and so the object is automatically treated as an rvalue (move candidate). If the expression that contains the last use is able to move from the rvalue, the move will happen automatically.
- If the name is a `forward` parameter, the object is automatically forwarded to preserve its constness and value category (`std::forward`-ed).
> Note: This gives language meaning to a naming convention of `guard` as a name prefix for "guard" stack objects, such as local `std::scoped_lock` objects, whose destructors are always the object's real last use.
For example:
``` cpp title="Definite last uses" linenums="1" hl_lines="13 16 19 21"
f: (
copy x: some_type,
move y: some_type,
forward z: some_type
)
= {
w: some_type = "y";
prepare(x); // NOT a definite last use
if something() {
process(y);
z.process(x); // definite last uses of x and z
}
else {
cout << z; // definite last use of z
}
transfer(y); // definite last use of y
offload(w); // definite last use of w
}
```
In this example:
- `x` has a definite last use on one path, but not another. Line 13 is a definite last use that automatically treats `x` as an rvalue. However, if the `#!cpp else` is taken, `x` gets no special automatic handling. Line 9 is not a definite last use because `x` could be used again where it is mentioned later on line 13.
- `y` has a definite last use on every path, in this case the same on all executions of the function. Line 19 is a definite last use that automatically treats `x` as an rvalue.
- `z` has a definite last use on every path, but unlike `y` it can be a different last use on different executions of the function. That's fine, each of lines 13 and 16 is a definite last use that automatically forwards the constness and value category of `z`.
- `w` has a definite last use on every path, in this case the same on all executions of the function. Line 21 is a definite last use that automatically treats `w` as an rvalue.
## Generality note: Summary of function defaults
There is a single function syntax, designed so we can just omit the parts we're not currently using.
For example, let's express in full verbose detail that `equals` is a function template that has two type parameters `T` and `U`, two ordinary `in` parameters `a` and `b` of type `T` and `U` respectively, and a deduced return type, and its body returns the result of `a == b`:
``` cpp title="equals: A generic function written in full detail (using no defaults)"
equals: (in a: T, in b: U) -> _ = { return a == b; }
```
We can write all that, but we don't have to.
First, `: type` is the default for template parameters, so we can omit it since that's what we want:
``` cpp title="equals: Identical meaning, now using the :type default for template parameters"
equals: (in a: T, in b: U) -> _ = { return a == b; }
```
So far, the return type is already using one common default available throughout Cpp2: the wildcard `_` (pronounced "don't care"). Since this function's body doesn't actually use the parameter type names `T` and `U`, we can just use wildcards for the parameter types too:
``` cpp title="equals: Identical meaning, now using the _ wildcard also for the parameter types"
equals: (in a: _, in b: _) -> _ = { return a == b; }
```
Next, `: _` is also the default parameter type, so we don't need to write even that:
``` cpp title="equals: Identical meaning, now using the :_ default parameter type"
equals: (in a, in b) -> _ = { return a == b; }
```
Next, `in` is the default [parameter kind](#parameters). So we can use that default too:
``` cpp title="equals: Identical meaning, now using the 'in' default parameter kind"
equals: (a, b) -> _ = { return a == b; }
```
We already saw that `{ return` ... `; }` is the default for a single-expression function body that deduces its return type:
``` cpp title="equals: Identical meaning, now using the { return ... } default body decoration"
equals: (a, b) -> _ = a == b;
```
Next, `#!cpp -> forward _` (fully deduced return type) is the default for single-expression functions that return something, and in this case will have the same meaning as `#!cpp -> _` :
``` cpp title="equals: Identical meaning, now using the -> _ = default for functions that return something"
equals: (a, b) = a == b;
```
Finally, at expression scope (aka "lambda/temporary") functions/objects aren't named, and the trailing `;` is optional:
``` cpp title="(not) 'equals': Identical meaning, but without a name as an unnamed function at expression scope"
:(a, b) = a == b
```
Here are some additional examples of unnamed function expressions:
``` cpp title="Some more examples of unnamed function expressions"
std::ranges::for_each( a, :(x) = std::cout << x );
std::ranges::transform( a, std::back_inserter(b), :(x) = x+1 );
where_is = std::ranges::find_if( b, :(x) = x == waldo$ );
```
> Note: Cpp2 doesn't have a separate "lambda" syntax; you just use the regular function syntax at expression scope to write an unnamed function, and the syntactic defaults are chosen to make such function expressions convenient to write. And because in Cpp2 every local variable [capture](expressions.md#captures) (for example, `waldo$` above) is written in the body, it doesn't affect the function syntax.