[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/DrizztDoUrden/cppfront/main/docs/cpp2/types.md [Back]  [Original]

# Types

## Overview

A user-defined `type` is written using the same **name `:` kind `=` value** [declaration syntax](../cpp2/declarations.md) as everything in Cpp2. The type's "value" is a `{}`-enclosed body containing more declarations.

In a `type`, data members are private by default, and functions and nested types are public by default. To explicitly declare a type scope declaration `#!cpp public`, `#!cpp protected`, or `#!cpp private`, write that keyword at the beginning of the declaration.

``` cpp title="Writing a simple type" hl_lines="1"
mytype: type =
{
    // data members are private by default
    x: std::string;

    // functions are public by default
    protected f: (this) = { do_something_with(x); }

    // ...
}
```

##  `#!cpp this`  The parameter name

**`#!cpp this`** is a synonym for the current object. Inside the scope of a type that has a member named `member`, `member` by default means `#!cpp this.member`.

> Note: In Cpp2, `#!cpp this` is not a pointer.

The name `#!cpp this` may only be used for the first parameter of a type-scope function (aka member function). It is never declared with an explicit `: its_type` because its type is always the current type.

`#!cpp this` can be an `in` (default), `inout`, `out`, or `move` parameter. Which you choose naturally determines what kind of member function is being declared:

- **`#!cpp in this`**: Writing `#!cpp myfunc: (this /*...*/)`, which is shorthand for `#!cpp myfunc: (in this /*...*/)`, defines a Cpp1 `#!cpp const`-qualified member function, because  `in` parameters are `#!cpp const`.

- **`#!cpp inout this`**: Writing `#!cpp myfunc: (inout this /*...*/)` defines a Cpp1 non-`#!cpp const` member function.

- **`#!cpp out this`**: Writing `#!cpp myfunc: (out this /*...*/)` defines a Cpp1 constructor... and more. (See below.)

- **`#!cpp move this`**: Writing `#!cpp myfunc: (move this /*...*/)` defines a Cpp1 `#!cpp &&`-qualified member function, or if there are no additional parameters it defines the destructor.

For example, here is how to write read-only member function named `print` that takes a read-only string value and prints this object's data value and the string message:

``` cpp title="The this parameter" hl_lines="4 6"
mytype: type = {
    data: i32;   // some data member (private by default)

    print: (this, msg: std::string) = {
        std::cout  Note: Generating `#!cpp inout this` (assignment) from `#!cpp out this` also generates **converting assignment** from converting construction, which is a new thing. Today in Cpp1, if you write a converting constructor from another type `X`, you may or may not write the corresponding assignment from `X`; in Cpp2 you will get that by default, and it sets the object to the same state as the converting constructor from `X` does.


###  Minimal functions generated by default

There are only two defaults the language will generate implicitly for a type:

- The only special function every type must have is the destructor. If you don't write it by hand, a public nonvirtual destructor is generated by default.

- If no `#!cpp operator=` functions other than the destructor are written by hand, a public default constructor is generated by default.

All other `#!cpp operator=` functions are explicitly written, either by hand or by opting into applying a metafunction (see below).

> Note: Because generated functions are always opt-in, you can never get a generated function that's wrong for your type, and so Cpp2 doesnt need to support "=delete" for the purpose of suppressing unwanted generated functions.

###  Memberwise by default

All copy/move/comparison `#!cpp operator=` functions are memberwise by default in Cpp2. That includes when you write memberwise construction and assignment yourself.

In a hand-written `#!cpp operator=`:

- The body must begin with a series of `member = value;` statements, one for each of the type's data members (including base classes) in declaration order.

- If the body does not mention a member in the appropriate place in the beginning section, by default the member's default initializer is used.

- In an assignment operator (`#!cpp inout this`), you can explicitly skip setting a member by writing `member = _;` where it would normally be set if you know you have a reason to set its value later instead or if the existing value needs to be preserved. (This is rare; for an example, see the generated implementation of the [`union` metafunction](metafunctions.md#union).)

For example:

``` cpp title="Memberwise operator= semantics" hl_lines="9-11 20-22"
mytype: type
= {
    //  data members (private by default)
    name:          std::string;
    social_handle: std::string = "(unknown)";

    //  conversion from string
    operator=: (out this, who: std::string) = {
        name = who;
        //  if social_handle is not mentioned, defaults to:
        //      social_handle = "(unknown)";

        //  now that the members have been set,
        //  any other code can follow...
        print();
    }

    //  copy/move constructor/assignment
    operator=: (out this, that) = {
        //  if neither data member is mentioned, defaults to:
        //      name = that.name;
        //      social_handle = that.social_handle;
        print();
    }

    print: (this) = std::cout  Note: This makes memberwise semantics symmetric for construction and assignment. In Cpp1, only non-copy/move constructors have a default, which is to initialize a member with its default initializer. In Cpp2, both constructors and assignment operators default to using the default initializer if it's a conversion function (non-`that`, aka non-copy/move), and using memberwise `member = that.member;` for copy/move functions.


##  `#!cpp operator`  Unified comparisons

To write comparison functions for your type, usually you just need to write either or both of `operator` and `operator==` with a first parameter of `this` and a second parameter of any type (usually `that` which is of the same type). If you omit the function body, a memberwise comparison will be generated by default.

`operator` must return one of `std::strong_ordering`, `std::partial_ordering`, or `std::weak_ordering`. It makes `=` comparisons available for your type. Prefer a strong ordering unless you have a reason to use a partial or weak ordering. If you write `operator` without a custom function body, `operator==` is generated for you.

`operator==` must return `bool`. It makes `==` and `!=` comparisons available for your type.

For example:

``` cpp title="Writing the  operator" hl_lines="5-7 13"
item: type = {
    x: i32         = ();
    y: std::string = ();

    operator: (this, that) -> std::strong_ordering;
        //  memberwise by default: first compares x  that.x,
        //  then if those are equal compares y  that.y

    // ...
}

test: (x: item, y: item) = {
    if x != y {         //  ok
        //  ...
    }
}
```

The above is the same as in Cpp1 because most of Cpp2's `#!cpp operator` feature has already been merged into ISO C++ (Cpp1). In addition, in Cpp2 comparisons with the same precedence can be safely chained, and always have the mathematically sound transitive meaning or else are rejected at compile time:

- **Valid chains: All `=`, or all `==`.** All mathematically sound and safe chains like `a = b < c` and `a != b != c` are compile time errors. They are "nonsense" because they are non-transitive; these chains do not imply any relationship between `a` and `c`.

- **Non-chains: Mixed precedence is not a chain.** Expressions like `a

Web Proxy Viewer  |  New URL  |  Original Page