OLD | NEW |
(Empty) | |
| 1 # Optional new/const |
| 2 |
| 3 Author: Lasse R.H. Nielsen ([lrn@google.com](mailto:lrn@google.com)) |
| 4 |
| 5 Version: 0.9 (2017-06-20) |
| 6 |
| 7 Status: Under discussion |
| 8 |
| 9 This informal specification documents a group of four related features. |
| 10 * Optional `const` |
| 11 * Optional `new` |
| 12 * Constructor tear-offs |
| 13 * Potentially constant auto-`new`/`const`. |
| 14 |
| 15 These are ordered roughly in order of priority and complexity. The constructor t
ear-offs feature effectively subsumes and extends the optional `new` feature. |
| 16 |
| 17 ## Optional const (aka. "const insertion") |
| 18 |
| 19 In current Dart code, every compile-time constant expression (except for annotat
ions) must be prefixed with a `const` keyword. This is the case, even when the c
ontext requires the expression to be a compile-time constant expression. |
| 20 |
| 21 For example, inside a `const` list or map, all elements must be compile-time con
stants. This leads to repeated `const` keywords in nested expressions: |
| 22 |
| 23 ```dart |
| 24 const dictionary = const { |
| 25 "a": const ["able", "apple", "axis"], |
| 26 "b": const ["banana", "bold", "burglary"], |
| 27 … |
| 28 }; |
| 29 ``` |
| 30 |
| 31 Here the `const` on the map and all the lists are *required*, which also means t
hat they are *redundant* (and annoying to have to write). |
| 32 |
| 33 The "optional const" feature allows you to omit the `const` prefix in places whe
re it would otherwise be required. It is effectively optional. |
| 34 |
| 35 The feature can also be seen as an "automatic const insertion" feature that auto
matically inserts the missing `const` where it's necessary. The end effect is th
e same - the user can omit writing the redundant `const`. |
| 36 This is somewhat precedented in that metadata annotations can be written as `@Fo
o(constantArg)`. |
| 37 |
| 38 Making `const` optional intersects perfectly with the "optional new" feature bel
ow, which does the same thing for `new`. |
| 39 |
| 40 Currently, the `const` prefix is used in front of map literals, list literals an
d *constructor calls*. |
| 41 Omitting the `const` prefix from list and map literals does not introduce a need
for new syntax, since that syntax is already used for plain list and map litera
ls. |
| 42 That doesn't apply to un-prefixed constructor calls – those do introduce a synta
x that isn't currently allowed: |
| 43 `MyClass<SomeType>.name(arg)`. The language allows generic function invocation,
which covers the unnamed constructor call `MyClass<SomeType>(arg)`, but it doesn
't allow applying type parameters to an identifier and *not* immediately calling
the result. |
| 44 |
| 45 To allow all const constructor invocations to omit the `const`, the grammar need
s to be extended to handle the case of `MyClass<SomeType>.name(arg)`. |
| 46 This syntax will only apply to unprefixed constructor invocations (at least unle
ss we also introduce type-instantiated generic method tear-offs). |
| 47 |
| 48 ### Prior discussion |
| 49 See http://dartbug.com/4046 and https://github.com/lrhn/dep-const/blob/master/pr
oposal.md |
| 50 |
| 51 The syntax for a constructor call is less ambiguous now than when these proposal
s were drafted, because generic methods have since been added to the language. T
he language has already decided how to resolve parsing of the otherwise ambiguou
s `Bar(Foo<int, bar>(42))`. |
| 52 |
| 53 |
| 54 ### Informal specification |
| 55 |
| 56 * An expression occurs in a "const context" if it is |
| 57 * a literal const `List`, const `Map` or const constructor invocation (`co
nst {...}`, `const [...]`, `const Symbol(...)`, `@Symbol(...)`), |
| 58 * a parameter default value, |
| 59 * the initializer expression of a const variable, |
| 60 * a case expression in a switch statement, or |
| 61 * is a sub-expression of an expression in a const context. |
| 62 |
| 63 That is: `const` introduces a const context for all its sub-expressions, as
do the syntactic locations where only const expressions can occur. |
| 64 |
| 65 * If a non-const `List` literal, non-const `Map` literal or invocation express
ion (including the new generic-class-member notation) occurs in a const context,
it is equivalent to the same expression with a `const` in front. That is, you d
on't have to write the `const` if it's required anyway. |
| 66 That is, an expression on one of the forms: |
| 67 * `Foo(args)` |
| 68 * `Foo<types>(args)` |
| 69 * `Foo.bar(args)` |
| 70 * `Foo<types>.bar(args)` |
| 71 * `prefix.Foo(args)` |
| 72 * `prefix.Foo<types>(args)` |
| 73 * `prefix.Foo.bar(args)` |
| 74 * `prefix.Foo<types>.bar(args)` |
| 75 * `[elements]` |
| 76 * `{mapping}` |
| 77 |
| 78 becomes valid in a `const` context. |
| 79 |
| 80 * The grammar is extended to allow `Foo<types>.id(args)` and `prefix.Foo<typeA
rguments>.id(args)` as an expression. They would not otherwise be valid expressi
ons anywhere in the current grammar. They still only work in a const context (it
's a compile-time error if they occur elsewhere, just not a grammatical error). |
| 81 |
| 82 * Otherwise this is purely syntactic sugar, and existing implementations can h
andle this at the syntactic level by inserting the appropriate synthetic `const`
prefixes. |
| 83 |
| 84 |
| 85 ## Optional new (aka. "new insertion") |
| 86 |
| 87 Currently, a call to a constructor without a prefixed `new` (or `const`) is inva
lid. With the optional const feature above, it would become valid in a const con
text, but not outside of a const context. |
| 88 |
| 89 So, if the class `Foo` has a constructor `bar` then `Foo.bar()` is currently a s
tatic warning/runtime failure (and strong mode compile-time error). |
| 90 |
| 91 Like for "optional const", we now specify such an expression to be equivalent to
`new Foo.bar()` (except in a const context where it's still equivalent to `cons
t Foo.bar()`). |
| 92 |
| 93 The "empty-named" constructor also works this way: `Foo()` is currently a runtim
e-error, so we can change its meaning to be equivalent to `new Foo()`. |
| 94 |
| 95 Like for optional const, we need to extend the grammar to accept `List<int>.fill
ed(4, 42)`. |
| 96 |
| 97 The `new` is optional, not prohibited. It may still be useful to write `new` as
documentation that this actually creates a new object. Also, some constructor na
mes might be less readable without the `new` in front. |
| 98 |
| 99 In the longer run, we may want to remove `new` so there won't be two ways to do
the same thing, but whether that is viable depends on choices about other featur
es that we are considering. |
| 100 |
| 101 Having optional `new` means that changing a static method to be a constructor is
not necessarily a breaking change. Since it's only optional, not disallowed, ch
anging in the other direction is a breaking change. |
| 102 |
| 103 ### Prior discussion |
| 104 |
| 105 See: http://dartbug.com/5680, http://dartbug.com/18241, http://dartbug.com/20750
. |
| 106 |
| 107 ### Informal specification |
| 108 |
| 109 * An expression on one of the forms: |
| 110 * `Foo(args)` |
| 111 * `Foo<types>(args)` |
| 112 * `Foo.bar(args)` |
| 113 * `Foo<types>.bar(args)` |
| 114 * `prefix.Foo(args)` |
| 115 * `prefix.Foo<types>(args)` |
| 116 * `prefix.Foo.bar(args)` |
| 117 * `prefix.Foo<types>.bar(args)` |
| 118 |
| 119 where `Foo`/`prefix.Foo` denotes a class and `bar` is a named constructor of
the class, and that is not in a const context, are no longer errors. |
| 120 |
| 121 * Instead they are equivalent to the same expression with a `new` in front. Th
is makes the `new` optional, but still allowed. |
| 122 * The grammar allows `prefix.Foo<typeArguments>.bar(args)` and `Foo<typeArgume
nts>.bar(args)` as expressions everywhere, not just inside const contexts. These
are not valid syntax in the current grammar. |
| 123 * Otherwise this is purely syntactic sugar, and existing implementations can h
andle this at the syntactic level by inserting a synthetic `new` in front of non
-const expressions that would otherwise try to invoke a constructor. This is sta
tically detectable. |
| 124 |
| 125 ## Constructor tear-offs |
| 126 |
| 127 With constructors being callable like normal static functions, it makes sense to
also allow them to be *torn off* in the same way. If `Foo.bar` is a constructor
of class `Foo`, then the *expression* `Foo.bar` will be a tear-off of the const
ructor (it evaluates to a function with the same signature as the constructor, a
nd calling the function will invoke the constructor with the same arguments and
an implicit `new`, and return the result). |
| 128 |
| 129 The tear-off of a constructor from a non-generic class is treated like a tear-of
f of a static method - it's a compile-time constant expression and it is canonic
alized. A generic class constructor tear-off is treated like the tear-off of an
instance method. It is not a compile-time constant and it isn't required to be c
anonicalized, but it must still be *equal* to the same constructor torn off the
same class instantiated with the same type parameters. |
| 130 |
| 131 For a non-named constructor, the expression `Foo` already has a meaning – it eva
luates to the `Type` object for the class `Foo` – so we can't use that to refer
to the unnamed constructor. |
| 132 |
| 133 We will introduce the notation `Foo.new`. This is currently a syntax error, so i
t doesn't conflict with any existing code. |
| 134 |
| 135 For named constructors, an expression like `Foo<int>.bar` (not followed by argum
ents like the cases above) is not currently allowed by the syntax, so there is n
o conflict. |
| 136 |
| 137 This tear-off syntax is something we want in any case, independently of the opti
onal new/const changes above. However, the syntax completely subsumes the option
al `new` feature; with tear-off syntax, `Foo.bar(42)` is just the tear-off `Foo.
bar` expression called as a function. You'd have to write `Foo.new(42)` instead
of just `Foo(42)` (which is an argument for re-purposing the `Foo` expression to
refer to the constructor instead of the type). |
| 138 That is, if we have constructor tear-offs, the only feature of optional `new` th
at isn't covered is calling the unnamed constructor. |
| 139 |
| 140 |
| 141 ### Informal specification |
| 142 |
| 143 * An expression *x* on one of the forms: |
| 144 * `Foo.new` |
| 145 * `Foo<types>.new` |
| 146 * `Foo.bar` |
| 147 * `Foo<types>.bar` |
| 148 * `prefix.Foo.new` |
| 149 * `prefix.Foo<types>.new` |
| 150 * `prefix.Foo.bar` |
| 151 * `prefix.Foo<types>.bar` |
| 152 |
| 153 where `Foo` and `prefix.Foo` denotes a class and `bar` is a constructor of `
Foo`, and the expression is not followed by arguments `(args)`, is no longer an
error. |
| 154 |
| 155 Not included are expressions like `Foo..new(x)` or `Foo..bar(x)`. This is ac
tually an argument against adding static cascades (`C..foo()..bar()` isn't curre
ntly a static call, it's a cascade on the `Type` object). |
| 156 |
| 157 * Instead of being an error, the expression evaluates to a function value |
| 158 * with the same signature as the constructor (same parameters, default val
ues, and having `Foo` or `Foo<types>` as return type), |
| 159 * which, when called with `args`, returns the same result as `new x'(args)
` where `x'` is `x` without any `.new`. |
| 160 * if `Foo` is not generic, the expression is a canonicalized compile-time
constant (like a static method). |
| 161 * If `Foo` is generic, the function is `==` to another tear off of the sam
e constructor from "the same instantiation" of the class (like an instance metho
d tear-off). We have to nail down what "the same instantiation" means, especiall
y if `void == Object` in our type system. |
| 162 * This feature be *implemented* by adding a static method for each non-generic
class constructor: |
| 163 |
| 164 ```dart |
| 165 class C { |
| 166 C(x1, …, xn) : … { body } |
| 167 static C C_asFunction(x1, … , xn) => new C(x1, … , xn); |
| 168 } |
| 169 ``` |
| 170 |
| 171 The tear-off of `C.new` is just `C_asFunction`. |
| 172 |
| 173 * … and adding a new helper class for each generic class with constructors: |
| 174 |
| 175 ```dart |
| 176 class D<T> { |
| 177 D(x1, …, xn) : … { body } |
| 178 } |
| 179 class D_constructors<T> { |
| 180 const D_constructors(); |
| 181 D_asFunction(x1, …, xn) => new D<T>(x1, …, xn); |
| 182 } |
| 183 ``` |
| 184 |
| 185 Then the tear-off of `D<T>.new` is `const D_constructors<T>().D_asFunction`.
If the type `T` is a non-const type parameter, the equality is harder to preser
ve, and the implementation might need to cache and canonicalize the `D_construct
ors` instances that it does the method tear-offs from, or some other clever hack
. |
| 186 |
| 187 * In strong mode, method type parameters are not erased, so the implementation
might be able to just create a closure containing the type parameter without a
helper class (but equality might be harder to get correct that way). |
| 188 * In most cases, implementations should be able to be more efficient than this
rewriting if they can refer directly to their representation of the constructor
. |
| 189 |
| 190 ### Alternatives |
| 191 Instead of introducing a new syntax, `Foo.new`, we could potentially re-purpose
the plain `Foo` to refer to the constructor and introduce a new syntax for the `
Type` object for the class, say the Java-esque `Foo.class`. It would be a major
breaking change, though, even if it could be mechanized. We should consider whet
her it's feasible to make this change, because it gives much better uniformity i
n what `Foo` means. |
| 192 |
| 193 An argument against this approach would be that `Foo` means the type in most oth
er cases, including `x is Foo`, and we plant to make the `is` oeprator work with
type objects as well as literal types. It would be problematic if `x is Foo` is
ambiguous, and we want to be backwards compatible. |
| 194 |
| 195 ## Optional new/const in *potentially* const expressions |
| 196 |
| 197 Together, the "optional const" and "optional new" features describe what happens
if you omit the operator on a constructor call in a const or normal expression.
However, there is one more kind of expression in Dart - the *potentially consta
nt expression*, which only occurs in the initializer list of a generative const
constructor. |
| 198 |
| 199 Potentially constant expressions have the problem that you can't write `new Foo(
x)` in them, because that expression is never constant, and you can't write `con
st Foo(x)` if `x` is a parameter, because `x` isn't always constant. The same pr
oblem applies to list and map literals. |
| 200 |
| 201 Allowing you to omit the `new`/`const`, and just write nothing, gives us a way t
o provide a new meaning to a constructor invocation (and list and map literals)
in a potentially const expression: Treat it as `const` when invoked as a const c
onstructor, and as `new` when invoking normally. |
| 202 |
| 203 This also allows you to use the *type parameters* of the constructor to create n
ew objects, like `class Foo<T> { final List<T> list; const Foo(int length) : lis
t = List<T>(length); }`. Basically, it can treat the type parameter as a potenti
ally constant variable as well, and use it. |
| 204 |
| 205 The sub-expressions must still all be potentially const, but that's not a big pr
oblem. |
| 206 |
| 207 It does introduce another problem that is harder to handle - avoiding infinite r
ecursion at compile-time. |
| 208 |
| 209 If a constructor can call another constructor as a potentially constant expressi
on, then it's possible to recurse deeply - or infinitely. |
| 210 |
| 211 Example: |
| 212 |
| 213 |
| 214 ```dart |
| 215 class C { |
| 216 final int value; |
| 217 final C left; |
| 218 final C right; |
| 219 const C(int start, int depth) |
| 220 : left = (depth == 0 ? null : C(start, depth - 1)), |
| 221 value = start + (1 << depth), |
| 222 right = (depth == 0 ? null : C(start + (1 << depth), depth - 1)); |
| 223 } |
| 224 ``` |
| 225 |
| 226 This class would be able to generate a complete binary tree of any depth as a co
mpile-time constant, using only *potentially constant* expressions and `const`/`
new`-less constructor calls. |
| 227 |
| 228 It's very hard to distinguish this case from one that recurses infinitely, and t
he latter needs to be able to be caught and rejected at compile-time. We need to
add some cycle-detection to the semantics to prevent arbitrary recursion. Since
no recursion is currently possible, it won't break anything. |
| 229 |
| 230 Proposed restriction: Don't allow a constant constructor invocation to invoke th
e same constructor again *directly*, where "directly" means: |
| 231 |
| 232 * as a sub-expression of an expression in the initializer list, or |
| 233 * *directly* in the initializer list of another const constructor that is invo
ked by a sub-expression in the initializer list. |
| 234 |
| 235 This transitively prevents the unfolding of the constructor calls to recurse wit
hout any limiting constraint. |
| 236 |
| 237 It does not prevent the invocation from referring to a const variable whose valu
e was created using the same constructor, so the following is allowed: |
| 238 |
| 239 |
| 240 ```dart |
| 241 const c0 = const C(0); |
| 242 const c43 = const C(43); |
| 243 class C { |
| 244 final v; |
| 245 const C(x) : v = ((x % 2 == 0) ? x : c0); // Silly but valid. |
| 246 } |
| 247 ``` |
| 248 |
| 249 |
| 250 The `const C(0)` invocation does not invoke `C` again, and the `const C(43)` inv
ocation doesn't invoke `C` again, it just refers to another (already created) co
nst value. |
| 251 |
| 252 As usual, a const *variable* cannot refer to itself when its value is evaluated. |
| 253 |
| 254 This restriction avoids infinite regress because the number of const variables a
re at most linear in the source code of the program while still allowing some re
ference to values of the same type. |
| 255 |
| 256 Breaking the recursive constraint at variables also has the advantage that a con
st variable can be represented only by its value. It doesn't need to remember wh
ich constructors were used to create that value, just to be able to give an erro
r in cases where that constructor refers back to the variable. |
| 257 |
| 258 This feature is more invasive and complicated than the previous three. If this f
eature is omitted, the previous three features still makes sense and should be i
mplemented anyway. |
| 259 |
| 260 ### Prior discussion |
| 261 |
| 262 See: [issue 18241](http://dartbug.com/18241) |
| 263 |
| 264 ### Informal specification |
| 265 |
| 266 In short: |
| 267 |
| 268 * A const constructor introduces a "potentially const context" for its initial
izer list. |
| 269 * This is treated similarly to a const context when the constructor is invoked
in a const expression and as normal expression when the constructor is invoked
as a non-const expression., |
| 270 * This means that `const` can be omitted in front of `List` literals, `Map` li
terals and constructor invocations. |
| 271 * All subexpressions of such expressions must still be *potentially const expr
essions*, otherwise it's still an error. |
| 272 * It is a compile-time error if a const constructor invoked in a const express
ion causes itself to be invoked again *directly* (immediately in the initializer
list or recursively while evaluating another const constructor invocation). It'
s not a problem to refer to a const variable that is created using the same cons
tructor. (This is different from what the VM currently does - the analyzer doesn
't detect cycles, and dart2js stack-overflows). |
| 273 * The grammar allows `type<typeArguments>(args)` and `type<typeArguments>.foo(
args)` as an expression in potentially const contexts, where the latter isn't cu
rrently valid syntax, and the former wouldn't be allowed in a const constructor. |
| 274 * This is not just syntactic sugar: |
| 275 * It makes const and non-const constructor invocations differ in behavior.
This alone can be simulated by treating it as two different constructors (perha
ps even rewriting it into two constructors, and change invocations to pick the c
orrect one based on context). |
| 276 * The const version of the constructor now allows parameters, including ty
pe parameters, to occur as arguments to constructor calls and as list/map member
s. This is completely new. |
| 277 * The language still satisfies that there is only one compile-time constan
t value associated with each `const` expression, but some expression in const co
nstructor initializer lists are no longer const expressions, they are just used
as part of creating (potentially nested) const values for the const expressions.
Effectively the recursive constructor calls need to be unfolded at each creatio
n point, not just the first level. Each such unfolding is guaranteed to be finit
e because it can't call the same constructor recursively and it stops at const v
ariable references (or literals). It *can* have size exponential in the code siz
e, though. |
| 278 |
| 279 |
| 280 |
| 281 ## Migration |
| 282 |
| 283 All the changes in this document are non-breaking - they assign meaning to synta
x that was previously an error, either statically or dynamically. As such, code
does not *need* to be migrated. |
| 284 |
| 285 We will want to migrate library, documentation and example code so they can serv
e as good examples. It's not as important as features that affect the actual API
. The most visible change will likely be that some constructors can now be torn
off as a const expression and used as a parameter default value. |
| 286 |
| 287 All other uses will occur inside method bodies or initializer expressions. |
| 288 |
| 289 Removing `new` is easy, and can be done by a simple RegExp replace. |
| 290 |
| 291 Removing nested `const` probably needs manual attention ("nested" isn't a regula
r property). |
| 292 |
| 293 Using constructor tear-offs will likely be the most visible change, with cases l
ike: |
| 294 |
| 295 |
| 296 ```dart |
| 297 map.putIfAbsent(42, HashSet<int>.new); // Rather than map.putIfAbsent(42, () =>
HashSet<int>())) |
| 298 bars.map(Foo.fromBar)... // rather than bars.map((x) => Foo.fromBar(x)) |
| 299 ``` |
| 300 |
| 301 Once the features are implemented, this can be either done once and for all, or
incrementally since each change is independent, but we should plan for it. |
| 302 |
| 303 ## Related possible features |
| 304 |
| 305 ### Type variables in static methods |
| 306 |
| 307 When you invoke a static method, you use the class name as a name-space, e.g., `
Foo.bar()`. |
| 308 |
| 309 If `Foo` is a generic class, you are not allowed to write `Foo<int>.bar()`. Howe
ver, that notation is necessary for optional `new`/`const` anyway, so we might c
onsider allowing it in general. The meaning is simple: the type parameters of a
surrounding class will be in scope for static methods, and can be used both in t
he signature and the body of the static functions. |
| 310 |
| 311 If the type parameter is omitted, it defaults to dynamic/is inferred to somethin
g, and it can be captured by the `Foo<int>.bar` tear-off. |
| 312 |
| 313 This is in agreement with the language specification that generally treats `List
<int>` as a class and the generic `List` class declaration as declaring a mappin
g from type arguments to classes. |
| 314 |
| 315 It makes constructors and static methods more symmetric. |
| 316 |
| 317 It's not entirely without cost - a static method on a class with a bound can onl
y be used if you can properly instantiate the type parameter with something sati
sfying the bound. A class like |
| 318 |
| 319 |
| 320 ```dart |
| 321 class C<T extends C<T>> { |
| 322 int compare(T other); |
| 323 static int compareAny(dynamic o1, dynamic o2) => o1.compare(o2); |
| 324 } |
| 325 ``` |
| 326 |
| 327 |
| 328 would not be usable as `C.compareAny(v1, v2)` because `C` cannot be automaticall
y instantiated to a valid bound. That is a regression compared to now, where any
static method can be called on any class without concern for the type bound. Th
is regression might be reason enough to drop this feature. |
| 329 |
| 330 Also, if the class type parameters are visible in members, including getters and
setters, it should mean that that *static fields* would have to exist for each
instantiation, not just once. That's so incompatible with the current behavior,
and most likely completely unexpected to users. This idea is unlikely to ever ha
ppen. |
| 331 |
| 332 ### Instantiated Type objects |
| 333 |
| 334 The changes in this document allows `Foo<T>` to occur: |
| 335 |
| 336 * Followed by arguments, `Foo<T>(args)` |
| 337 * Followed by an identifier, `Foo<T>.bar` (and optionally arguments). |
| 338 * Followed by `new`, `Foo<T>.new`. |
| 339 |
| 340 but doesn't allow `Foo<T>` by itself, not even for the non-named constructor. |
| 341 |
| 342 The syntax is available, and needs to be recognized in most settings anyway, so
we could allow it as a type literal expression. That would allow the expression
`List<int>` to evaluate to the *Type* object for the class *List<int>*. It's bee
n a long time (refused) request: [issue 23221](http://dartbug.com/23221). |
| 343 |
| 344 The syntax will also be useful for instantiated generic method tear-off like `va
r intContinuation = future.then<int>;` |
| 345 |
| 346 ### Generic constructors |
| 347 |
| 348 We expect to allow generic constructors. |
| 349 Currently constructors are not generic the same way other methods are. Instead t
hey have access to the class' type parameters, but they can't have separate type
parameters. |
| 350 |
| 351 We plan to allow this for name constructors, so we can write: |
| 352 ```dart |
| 353 class Map<K, V> { |
| 354 … |
| 355 factory Map.fromIterable<S>( |
| 356 Iterable<S> values, {K key(S value), K value(S value)}) { |
| 357 … |
| 358 } |
| 359 … |
| 360 } |
| 361 ``` |
| 362 Having generic constructors shouldn't add more syntax with optional `new` becaus
e it uses the same syntax as generic method invocation. If anything, it makes th
ings more consistent. |
| 363 |
| 364 ### Inferred Constant Expression |
| 365 |
| 366 An expression like `Duration(seconds: 2)` can be prefixed by either `const` or `
new`. The optional `new` feature would make this create a new object for each ev
aluation. |
| 367 However, since all arguments are constant and the constructor is `const`, it cou
ld implicitly become a `const` expression instead. |
| 368 |
| 369 This has some consequences – if you actually need a new object each time (say a
`new Object()` to use as a marker or sentinel), you would now *have to* write `n
ew` to get that behavior. This suggests that if we introduce this feature at all
, we should do so at the same time as optional `new`, it would be a breaking cha
nge to later change `Object()` from `new` to `const`. |
| 370 |
| 371 This feature also interacts with optional const. An expression like `Foo(Bar())`
, where both `Foo` and `Bar` are `const` constructors, can be either `const` or
`new` instantiated. It would probably default to `new`, but writing `const` befo
re either `Foo` or `Bar` would make the other be inferred as constant as well. I
t's not clear that this is predictable for users (you can omit either, but not b
oth `const` prefix without changing the meaning). |
| 372 |
| 373 ### Revisions |
| 374 |
| 375 0.5 (2017-02-24) Initial version. |
| 376 |
| 377 0.6 (2017-06-08) Added "Migration" section, minor tweaks. |
| 378 |
| 379 0.7 (2017-06-19) Reordered features, added more related features. |
| 380 |
| 381 0.8 (2017-06-20) Fix-ups and typos. |
| 382 |
| 383 0.9 (2017-06-20) Add more complications for using `Foo` as the tear-off syntax. |
OLD | NEW |