You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* types
* metafunctions
* Taking a pass over the updates, and adding that `that` must be `in` or `move`
* Remove TODO comment for now
Not sure of its meaning, we can add again later
---------
Co-authored-by: Herb Sutter <[email protected]>
Copy file name to clipboardExpand all lines: docs/cpp2/metafunctions.md
+8-6Lines changed: 8 additions & 6 deletions
Original file line number
Diff line number
Diff line change
@@ -13,7 +13,7 @@ The most important thing about metafunctions is that they are not hardwired lang
13
13
14
14
## Applying metafunctions
15
15
16
-
Metafunctions provide an easy way for a type author to opt into a group of defaults, constraints, and generated functions: Just write `@name` afer the `:` of a declaration, where `name` is the name of the metafunctions. This lets the type author declare (and the human reader see) the intent up front: "This isn't just any `type`, this is a `@value type`" which automatically gives the type default/copy/move construction and assignment, `<=>` with `std::strong_ordering` comparisons, and guarantees that it has a public destructor and no protected or virtual functions:
16
+
Metafunctions provide an easy way for a type author to opt into a group of defaults, constraints, and generated functions: Just write `@name` afer the `:` of a declaration, where `name` is the name of the metafunction. This lets the type author declare (and the human reader see) the intent up front: "This isn't just any `type`, this is a `@value type`" which automatically gives the type default/copy/move construction and assignment, `<=>` with `std::strong_ordering` comparisons, and guarantees that it has a public destructor and no protected or virtual functions:
17
17
18
18
```cpp title="Using the value metafunction when writing a type" hl_lines="1"
19
19
point2d: @value type = {
@@ -95,7 +95,7 @@ skat_game: @enum<i16> type = {
95
95
96
96
Consider `hearts`: It's a member object declaration, but it doesn't have a type (or a default value) which is normally illegal, but here it's okay because the `@enum<i16>` metafunction fills them in: It iterates over all the data members and gives each one the underlying type (here explicitly specified as `i16`, otherwise it would be computed as the smallest signed type that's big enough), and an initializer (by default one higher than the previous enumerator).
97
97
98
-
Unlike C `enum`, this `@enum` is scoped and strongly typed (does not implicitly convert to the underlying type.
98
+
Unlike C `enum`, this `@enum` is scoped and strongly typed (does not implicitly convert to the underlying type).
99
99
100
100
Unlike C++11 `enum class`, it's "just a `type`" which means it can naturally also have member functions and other things that a type can have:
101
101
@@ -141,10 +141,11 @@ file_attributes: @flag_enum<u8> type = {
141
141
// name_or_number is declaratively a safe union/variant type:
142
142
// it has a discriminant that enforces only one alternative
143
143
// can be active at a time, members always have a name, and
144
-
// each member has .is_member() and .member() accessors...
145
-
// the word "union" carries all that meaning as a convenient
146
-
// and readable opt-in without hardwiring "union" specially
147
-
// into the language
144
+
// each member has .is_member(), .set_member(), and .member()
145
+
// accessors using the member name... the word "union"
146
+
// carries all that meaning as a convenient and readable
147
+
// opt-in without hardwiring "union" specially into the
148
+
// language
148
149
//
149
150
name_or_number: @union type = {
150
151
name: std::string;
@@ -155,6 +156,7 @@ main: () = {
155
156
x: name_or_number = ();
156
157
157
158
x.set_name("xyzzy"); // now x is a string
159
+
assert( x.is_name() );
158
160
std::cout << x.name(); // prints the string
159
161
160
162
// trying to use x.num() here would cause a Type safety
Copy file name to clipboardExpand all lines: docs/cpp2/types.md
+16-11Lines changed: 16 additions & 11 deletions
Original file line number
Diff line number
Diff line change
@@ -110,7 +110,7 @@ A `this` parameter of an `operator=` function can additionally be declared as:
110
110
111
111
## `operator=` — Construction, assignment, and destruction
112
112
113
-
All value operations are spelled `operator=`, including construction, assignment, and destruction. `operator=` sets the value of `this` object, so the `this` parameter can be pass as anything but `in` (which would imply `const`):
113
+
All value operations are spelled `operator=`, including construction, assignment, and destruction. `operator=` sets the value of `this` object, so the `this` parameter can be passed as anything but `in` (which would imply `const`):
114
114
115
115
-**`out this`:** Writing `operator=: (out this /*...*/ )` is naturally both a constructor and an assignment operator, because an `out` parameter can take an uninitialized or initialized argument. If you don't write a more-specialized `inout this` assignment operator, Cpp2 will use the `out this` function also for assignment.
116
116
@@ -120,11 +120,14 @@ All value operations are spelled `operator=`, including construction, assignment
120
120
121
121
Unifying `operator=` enables usable `out` parameters, which is essential for composable guaranteed initialization. We want the expression syntax `x = value` to be able to call a constructor or an assignment operator, so naming them both `operator=` is consistent.
122
122
123
+
TODO Return type of assignment operator?
124
+
123
125
> Note: Writing `=` always invokes an `operator=` (in fact for a Cpp2-authored type, and semantically for a Cpp1-authored type). This avoids the Cpp1 inconsistency that "writing `=` calls `operator=`, except when it doesn't" (such as in a Cpp1 variable initialization). Conversely, `operator=` is always invoked by `=` in Cpp2.
124
126
127
+
125
128
### `that` — A source parameter
126
129
127
-
All functions can have a **`that`** is a synonym for the object to be copied/moved from. Like `this`, at type scope it is never declared with an explicit `: its_type` because its type is always the current type.
130
+
All type-scope functions can have **`that`**as their second parameter, which is a synonym for the object to be copied/moved from. Like `this`, at type scope it is never declared with an explicit `: its_type` because its type is always the current type. Unlike `this`, `that` is always passed as a `in` (the default) or `move` parameter.
128
131
129
132
`that` can be an `in` (default) or `move` parameter. Which you choose naturally determines what kind of member function is being declared:
130
133
@@ -157,7 +160,7 @@ In Cpp1 terms, they can be described as follows:
157
160
158
161
-**M2 is preferred over A2.** Both M2 and A2 can generate a missing `(inout this, move that)` function. If both options are available, Cpp2 prefers to use M2 (generate move assignment from copy assignment, which could itself have been generated from copy construction) rather than A2 (generate move assignment from move construction). This is because M2 is a better fit: Move assignment is more like copy assignment than like move construction, because assignments are designed structurally to set the value of an existing `this` object.
159
162
160
-
The most general `operator=` with `that` is `(out this, that)`. In Cpp1 terms, it generates all four combinations of { copy, move } x { constructor, assignment }. This is often sufficient, so you can write all these value-setting just once. If you do want to write a more specific version that does something else, though, you can always write it too.
163
+
The most general `operator=` with `that` is `(out this, that)`. In Cpp1 terms, it generates all four combinations of { copy, move } x { constructor, assignment }. This is often sufficient, so you can write all these value-setting functions just once. If you do want to write a more specific version that does something else, though, you can always write it too.
161
164
162
165
> Note: Generating `inout this` (assignment) from `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.
163
166
@@ -169,21 +172,23 @@ There are only two defaults the language will generate implicitly for a type:
169
172
170
173
- 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.
171
174
172
-
- If no `operator=` functions are written by hand, a public default constructor is generated by default.
175
+
- If no `operator=` functions other than the destructor are written by hand, a public default constructor is generated by default.
173
176
174
177
All other `operator=` functions are explicitly written, either by hand or by opting into applying a metafunction (see below).
175
178
176
179
> Note: Because generated functions are always opt-in, you can never get a generated function that's wrong for your type, and so Cpp2 doesn’t need to support "=delete" for the purpose of suppressing unwanted generated functions.
177
180
178
181
### Memberwise by default
179
182
180
-
All copy/move/comparison `operator=` functions are memberwise by default in Cpp2. That includes when you write memberwise construction and assignment yourself. In a hand-written `operator=`:
183
+
All copy/move/comparison `operator=` functions are memberwise by default in Cpp2. That includes when you write memberwise construction and assignment yourself.
184
+
185
+
In a hand-written `operator=`:
181
186
182
-
- The body must begin with a series of `member = value;` statements, one for each of the type's data members in order.
187
+
- 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.
183
188
184
-
- If the body does not mention a member, by default the member's default initializer is used.
189
+
- 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.
185
190
186
-
- In an assignment operator (`inout this`), you an 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.
191
+
- In an assignment operator (`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.
187
192
188
193
For example:
189
194
@@ -224,12 +229,12 @@ main: () = {
224
229
y = x; // copy assign
225
230
z := (move x); // move construct
226
231
z = (move y); // move assign
227
-
x.print(); // [] [] - moved from
228
-
y.print(); // [] [] - moved from
232
+
x.print(); // "value is [] []" - moved from
233
+
y.print(); // "value is [] []" - moved from
229
234
}
230
235
```
231
236
232
-
> 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 for if it's a conversion function (non-`that`, aka non-copy/move), and using memberwise `member = that.member;` for copy/move functions.
237
+
> 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.
0 commit comments