-
Notifications
You must be signed in to change notification settings - Fork 251
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SUGGESTION] Literal suffixes are constructors. #455
Comments
6. Similarity and comparison
|
Been trying to give some feedback for some days but idk what to say. This suggestion builds up on Herb's {constructor × assignment} unification by making suffixes as contructor which, when you think about it makes a lot of sense. using namespace std::literals;
//Cpp example
auto str1 = "hi y'all"s;
auto str2 = std::string{"hi again"};
//both work using namespace std::literals;
//cpp2 example
str1 := "hi a third time"std::string;
str2 := "last hi"s; //would this work? Other than that, I think this suggestion is great (I would like anything that prevents me from writing the type between : and =) But I would also like to see how issue #451 is solved, maybe Herb could come up with something combined with this that also keeps the operator= as a binary operator. |
Thanks for your feedback. Yes, that Cpp2 example would work. Herb stated in this comment, he want to support consuming UDLs, but he didn't decide on whether or not to support authoring UDLs yet. UDLs and TypesIt's possible to have UDLs with the same name of types. In this case, types will be prefered over UDLs. For example: // abc: type;
// UDL in Cpp1
abc operator ""abc(const char *str, std::size_t len) {
return (: std::string = (str, len));
}
// Type declaration in Cpp2
abc: type = {
operator=: (out this, value: std::string) = {}
}
main: () = {
// It won't call UDL function.
// It would call the type's constructor.
object: = "text"abc;
} On the other hand, can UDLs be used in place of types? Two options may be considered:
// UDL in Cpp1
unsigned long long operator ""ull(unsigned long long value) {
return value;
}
main: () = {
// It would call UDL function: `operator ""ull(1'000)`
object: ull = 1'000;
}
Option 1 is generalized for object construction, similar to how UFCS works on functions. Option 1 would make UDLs to behave like they are non-member constructors, IMO it's better than option 2. Declaration syntax in Cpp2;UDLs are Non-member Constructors.But if the plan is to support authoring them, if we look at how semantically they are related to types' constructors, the following syntax seems reasonable for them, especially if the plan is to allow UDLs to be used in place of types: // in Cpp1:
// RETURNTYPE operator ""SUFFIX(ARGTYPE ARG) {...}
SUFFIX: (ARG: ARGTYPE) -> type == RETURNTYPE = {
// -- statements...
} That means, they are functions in which their return type is a type alias. For example: // in Cpp1:
// unsigned long long operator ""ull(unsigned long long value) {...}
ull: (value: ulonglong) -> type == ulonglong = {
return value;
}
main: () = {
x: = 1'000ull;
} In this case, UDLs in Cpp1 are changed to mean Non-member Constructors in Cpp2. |
Shouldn't your last bit of cpp2 code
main: () = {
x: = 1'000ull;
}
Have a function call, rather than a UDL?
main: () = {
x: = 1'000.ull();
}
On 18 May 2023 08:29:49 Sadeq ***@***.***> wrote:
Thanks for your feedback. Yes, that Cpp2 example would work. Herb stated in [this comment][1], he want to support consuming UDLs, but he didn't decide on whether or not to support authoring UDLs yet.
UDLs
It's possible to have UDLs with the same name of types. In this case, types will be prefered over UDLs. For example:
// abc: type;
// UDL in Cpp1
abc operator ""abc(const char *str, std::size_t len) {
return (: std::string = (str, len));
}
// Type declaration in Cpp2
abc: type = {
operator=: (out this, value: std::string) = {}
}
main: () = {
// It won't call UDL function.
// It would call the type's constructor.
object: = "text"abc;
}
On the other hand, can UDLs be used as types? Two approaches may be considered:
1. UDLs can be used in place of types too (but of course, types will be prefered over UDLs if they have the same name). The return type of UDL functions will be used to treat them as types. For example:
// UDL in Cpp1
unsigned long long operator ""ull(unsigned long long value) {
return value;
}
main: () = {
// It would call UDL function.
object: ull = 1'000;
}
1. UDLs cannot be used as types. If the plan is not to support authoring UDLs in Cpp2, a simple type alias is a good alternative:
// Type alias in Cpp2
str: == std::string;
object = "text"str;
UDLs are Non-member Constructors (declaration syntax in Cpp2)
But if the plan is to support authoring them, if we look at how semantically they are related to types' constructors, the following syntax seems reasonable for them, especially if the plan is to allow UDLs to be used in place of types:
// in Cpp1:
// RETURNTYPE operator ""SUFFIX(ARGTYPE ARG) {...}
SUFFIX: (ARG: ARGTYPE) -> type == RETURNTYPE = {
// -- statements...
}
That means, they are functions in which their return type is a type alias. For example:
// in Cpp1:
// unsigned long long operator ""ull(unsigned long long value) {...}
ull: (value: ulonglong) -> type == ulonglong = {
return value;
}
main: () = {
x: = 1'000ull;
}
In this case, UDLs in Cpp1 are changed to mean Non-member Constructors in Cpp2.
—
Reply to this email directly, view it on GitHub<#455 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQP4VHME7YBSDE3OXO3XGXFWVANCNFSM6AAAAAAYAQFN4M>.
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
It doesn't need parenthesis, because the idea is to make I have to explain that if the constructor requires multiple arguments, it would be called like On the other hand, UDL functions cannot have multiple parameters, therefore it won't be called like |
I've corrected that to this:
Because optional parenthesis and allowing UDLs to work on expressions, will help without any conflict or ambiguity. |
Ah, apologies, I believe thanks to UFCS the
.ull()
Already works in cpp2, so why add another way of doing the same thing?
On 18 May 2023 08:58:34 Sadeq ***@***.***> wrote:
Shouldn't your last bit of cpp2 code
main: () = { x: = 1'000ull; }
Have a function call, rather than a UDL?
main: () = { x: = 1'000.ull(); }
It doesn't need parenthesis, because the idea is to make ...TYPE or ...SUFFIX to call TYPE's constructor or SUFFIX UDL function respectively.
I have to explain that if the constructor requires multiple arguments, it would be called like (arg1, arg2, ...)TYPE.
On the other hand, UDL functions cannot have multiple parameters, therefore it won't be (arg1, arg2, ...)SUFFIX, because SUFFIX would only work on a single literal without parenthesis, so (10)suffix is wrong and 10suffix is correct.
—
Reply to this email directly, view it on GitHub<#455 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQOPCEBFZKSAUMA2MTDXGXJCFANCNFSM6AAAAAAYAQFN4M>.
You are receiving this because you commented.Message ID: ***@***.***>
|
If Herb accepts to support authoring UDLs like they are non-member constructors, in this case also |
Because:
Those reasons are from this issue. |
Yes, user defined literals are a type of function call, just one with a weird syntax (what does operator"" have to do with numerical literals anyway?!) and unique rules that need to be taught, UFCS seems ideal for this IMO.
literal.function() is the same as function(literal), as per UFCS, the same as everywhere, I don't see any room for ambiguity, unless there is a function and a type with the same name and signature, but then I'm not sure if that is valid anyway?
Your inheritance example, why do your base classes both have constructors requiring an instance of the child type? Is this valid code? Also this example doesn't contain literals so I'm not sure how it is relevant?
Where you say
a.A(args)) may create a variable or may call a function (or function object).
A is not a literal in this example, and A(args) doesn't seem to be a UDL either, but for completeness
1.MyIntegerType()
1.funcReturningMyIntegerType()
Being replaced by
MyIntegerType(1)
funcReturningMyIntegerType(1)
Seems fine, and in both cases this is really very equivalent. In both cases an instance is created, and in both cases a function call occurs, it just so happens that one of those calls is a constructor function.
Are you perhaps trying to report a bug with UFCS with relation to classes and multiple inheritance?
Or perhaps I need to log into GitHub and stop doing all this via email... Apologies if I'm missing some greater context here
On 18 May 2023 09:16:46 Sadeq ***@***.***> wrote:
Ah, apologies, I believe thanks to UFCS the .ull() Already works in cpp2, so why add another way of doing the same thing?
Because:
It's surprising to me, because:
1. UFCS is about Unifying Function Call Syntax, and suddenly it works with types.
2. It doesn't feel expressive enough for a context-free language. : Type = (args) creates a variable, but A(args) (also a.A(args)) may create a variable or may call a function (or function object).
3. It's like accessing a base class within multiple inheritance, e.g. a.Base::call() in Cpp1.
Also it would conflict with multiple inheritance in Cpp2, it depends on how we would access base types:
Base1: type = {
operator=: (out this) = {}
operator=: (out this, v: x) = {}
operator(): (this) -> int = 0;
}
Base2: type = {
operator=: (out this) = {}
operator=: (out this, v: x) = {}
operator(): (this) -> int = 0;
}
x: type = {
this: Base1 = ();
this: Base2 = ();
variable: Base1 = ();
operator(): (this) -> int = {
// It calls operator().
m: = this.variable();
// Does it call operator() from Base1?
// or calls the constructor with `Base1(this)`?
// It's ambiguous because of UFCS on types.
n: = this.Base1();
return 0;
}
}
In example above, this::Base1() can be another syntax option, but that resembles scope resolution operator (e.g. namespace::... or type::...) which doesn't look uniform to how we access members of this.
It would complicate the language, similar to how Type(...) has complicated Cpp1 for object construction and function declaration in Most Vexing Parse. It's better to distinguish types from functions and variables syntactically in addition to semantically.
I couldn't find main reasons that why it's surprising to me. Now I've found them:
1. UFCS is syntactically and semantically incorrect for types.
* UFCS is about to unify function(a, args) (non-member functions) with a.function(args) (member functions).
* It's important to note that both of them are valid syntax for functions without UFCS.
* On the other hand, Type(a, args) is unified with a.Type(args).
* But the problem with a.Type(args) is that itself is not a valid syntax without UFCS!
* It must be A::Type(args) to be a valid nested type, because nested types always need scope resolution operator.
So UFCS on types would unify Type(a) (object construction) with an invalid syntax a.Type() (nested type which has to be A::Type()). That's the reason why I think UFCS on types are incorrect.
1. It's inconsistent with nested types, thus what's the point of UFCS on types? For example:
A: type = {
X: type = {}
}
B: type = {
operator=: (out this, a: A) = {}
}
main: () = {
a: A = ();
// It works.
// It's equal to `B(a)`.
m: = a.B();
// a.B() == B(a)
// ERROR! It doesn't work.
// It must be `A::X()`.
n: = a.X();
// a.X() != A::X(a)
}
So a.Type(arg) would lead to surprises on types, because it doesn't work on nested types.
UFCS on types is in contrast to the purpose of operator. which is to access members!
1. UFCS on types would make member functions to conflict with a.SOMETHING(args).
Member functions and types are completely different, but unwillingly they will impact each other. It's is in contrast with UFCS for functions in which it only impacts on what function to call.
abc: type = {
klass: (this) = {}
}
klass: type = {
operator=: (out this, v: abc) = {}
}
main: () = {
a: abc = ();
// It conflicts...
// Does it call the constructor of `klass`?
// or it calls the member function `klass`?
a.klass();
}
In this example, the meaning of a.klass() would be ambiguous.
1. For any type named klass, semantically a.klass() is inconsistent with member access.
In contrast, o.func() and func(o) for functions are semantically consistent with member access, the first argument is the object.
But a.klass() and klass(a) for types are semantically inconsistent with member access, the first argument is not the object, it's just an argument which shouldn't be used like an object. Types don't have enough relation to UFCS.
a.Type() --> operator=(out this, a)
a.func() --> func(a) // `this = a` for member functions
Those reasons are from this issue<#284>.
—
Reply to this email directly, view it on GitHub<#455 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQJ6CCFFRHFSLJPK4S3XGXLGNANCNFSM6AAAAAAYAQFN4M>.
You are receiving this because you commented.Message ID: ***@***.***>
|
I'm agree with you except the part about UFCS. Instead of using UFCS to replace UDLs, let's fix that problems. Cpp2 can have a different syntax for declaring UDLs. The following is just an example (its syntax can be anything else): suffix: (value: ulong) -> type == SomeType = {
// statements...
} I should mention I don't suggest to support authoring UDLs in Cpp2 (it's just a possibility to consider). I suggest to change the syntax of object construction from
Yes, that's the problem. It works, but it doesn't worth it. In a nutshell, the problems with
|
Now, let's consider these examples of how Connection: type = {
operator=: (out this, timeout: uint) = {}
operator=: (out this, timeout: uint, proxy: my::proxy) = {}
operator=: (out this, encrypted: bool, timeout: uint, proxy: my::proxy) = {}
}
main: () = {
x: = 2000.Connection();
// Are they related to UFCS and UDLs? No.
y: = 2000.Connection(my::proxy());
z: = true.Connection(2000, my::proxy());
} IMO that code is unreadable. The problem with |
Thanks, this is really clear now I understand.
UDL has historically required a function definition, which still works in cpp2 via UFCS, however you want to succinctly specify what the type is of the literal, without the need to create a function, and then call it.
In cpp1 you can use size_t{1} with the added benefit that the literal is bounds checked.
In cpp2 the following is difficult
foo : uint64 = ~0;
bar : uint64 = 0xffffffffffffffff;
Are these equivalent values? Or is ~0 an int32?
UFCS way (requires an extra function definition)
ull := (in x : uint64) { return x; }
foo : uint64 = ~0.ull();
Your way (presumably you're expecting the constructor to take precedence over the ~operstor)
foo := ~0uint64
Built in types don't have a constructor so does this work in cppfront?
foo := ~uint64(0)
I see where you're coming from, however it does add to the concept count of the language.
How does your proposition handle the difference between built in and user defined types?
On 18 May 2023 12:20:53 Sadeq ***@***.***> wrote:
Yes, user defined literals are a type of function call, just one with a weird syntax (what does operator"" have to do with numerical literals anyway?!) and unique rules that need to be taught, UFCS seems ideal for this IMO.
I'm agree with you except the part about UFCS. Instead of using UFCS to replace UDLs, let's fix that problems. Cpp2 can have a different syntax for declaring UDLs. The following is just an example (its syntax can be anything else):
suffix: (value: ulong) -> type == SomeType = {
// statements...
}
I should mention I don't suggest to support authoring UDLs in Cpp2 (it's just a possibility to consider). I suggest to change the syntax of object construction from TYPE(...) to (...)TYPE, therefore UDLs would be completely replaced with types.
literal.function() is the same as function(literal), as per UFCS, the same as everywhere, I don't see any room for ambiguity, ...
Yes, that's the problem. It works, but it doesn't worth it. In a nutshell, the problems with a.Type(args) are that:
* Syntactically it's inconsistent with member access operator (aka operator dot), because a doesn't have member Type.
* Syntactically it's inconsistent with scope resolution operator (aka ::) for referring to types.
* Syntactically it's wrong, because a is not the first argument of Type's constructor in operator=: (out this, args).
* Syntactically it's not context-free.
* The compiler (not transpiler) and the programmer must look up for Type declaration to see if that's a type or a callable.
* Semantically it's inconsistent with UFCS, because they do completely different things, in this way:
* UFCS on functions: a is the object to work with it.
* UFCS on types: a is not the object to work with it, a and args are arguments to construct a new object.
* Semantically it's meaningless, because a is always exactly the same as args, that's a useless visual separation.
—
Reply to this email directly, view it on GitHub<#455 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQJ7FY777GXAW7A4JQDXGYAY3ANCNFSM6AAAAAAYAQFN4M>.
You are receiving this because you commented.Message ID: ***@***.***>
|
I agree, but again, there is no implicit this in cppfront, so I don't think literal calls via UFCS can call type members anyway
On 18 May 2023 12:57:47 Sadeq ***@***.***> wrote:
Now, let's consider these examples of how a.Type(args) may go wrong:
Connection: type = {
operator=: (out this, timeout: uint) = {}
operator=: (out this, timeout: uint, proxy: my::proxy) = {}
operator=: (out this, encrypted: bool, timeout: uint, proxy: my::proxy) = {}
}
main: () = {
// No! It's not 2000 connections! It's `Connection(2000)`.
x: = 2000.Connection();
// Are they related to UFCS and UDLs? No.
y: = 2000.Connection(my::proxy());
z: = true.Connection(2000, my::proxy());
}
IMO that code is unreadable.
—
Reply to this email directly, view it on GitHub<#455 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQMOV7NCMN5SGGJBL6TXGYFDTANCNFSM6AAAAAAYAQFN4M>.
You are receiving this because you commented.Message ID: ***@***.***>
|
Yes, that works with
It changes the syntax of one concept from
They wouldn't be a separate concept. Infact there wouldn't be any built-in literal prefixes and suffixes. All of them would be UDLs for built-in types (if needed). They can be used with types and type aliases together in a similar way: ull: type == ulonglong;
something: type = {
operator=: (out this, value: ull) = {}
}
// -- UDL (aka Non-member Constructor) declaration example in Cpp2
suffix: (value: ull) -> type == something = (value)something;
main: () = {
// -- All of them are valid.
a: = (10)int;
b: = 10int;
c: = 10ull;
d: = 10something;
e: = (10ull)something;
f: = (10ull)suffix;
g: = (10)suffix;
h: = 10suffix;
}
If I understand your response correctly, they would be called as reported in this comment by @JohelEGP, because when Cpp2 transforms |
I have to clarify that with // This separation between `true` and other arguments are meaningless.
// Using TYPE(...) and UFCS on types for object construction:
x0: = true.Connection(2000, my::proxy());
// All arguments have the same behaviour on object construction.
// Using (...)TYPE for object construction:
x1: = (true, 2000, ()my::proxy)Connection; In But in |
That's not a problem exclusive to types.
|
That's a problem exclusive to types, because the first argument of member functions is already an object |
You're mixing up two things. As for the |
Maybe I didn't understand your comment correctly, but that's what I'm trying to explain that I don't have a problem with how Consider I want to explain about UFCS on types for novice programmers in the following paragraphs.
function: (value: int, arg: int) = {}
x: = 10.function(10);
Something: type = {
function: (this, arg: int) = {}
}
a: Something = ();
x: = a.function(10);
Something: type = {
operator=: (out this, arg1: int, arg2: int) = {}
}
x: = 10.Something(10); OK. This example is one of the reasons I think UFCS on types are not natural. You get visually similar syntax for both UFCS on functions |
That could be convincing.
I make no conclusions so far. |
I think authoring UDLs shouldn't be a thing if this suggestion is implemented, could make thing confusing IMO. |
@JohelEGP Good point. I change them to Cpp2 function signatures: // `obj` is not `this`. It can't be the object.
x: Type = (obj, 0); //--> (out this, obj, 0)
x: =: Type = (obj, 0); //--> (out this, obj, 0)
x: = Type(obj, 0); //--> (out this, obj, 0)
// `obj` can be the object.
x: = func(obj, 0); //--> func(inout this = obj, 0)
// `obj` is not `this`. It can't be the object.
// Why is `obj` treated like the object?
// Whereas `out this` is the object.
x: = obj.Type(0); //--> (out this, obj, 0)
// `obj` can be the object.
x: = obj.func(0); //--> func(inout this = obj, 0) So both
|
@AbhinavK00 You're right. The syntax is like literal suffixes. They are expressive for object construction. I'm agree that Authoring UDLs is only a possibility to consider. As you said, it would increase concept count. By the way, UDLs can be Non-member Constructors in my suggestion. |
I have to mention, |
I'm thinking about this use case... what if Cpp2 supports it too? Consider we already have Function Chaining in C++: x: = fetch("something").filter(10).sort(true); With x: = fetch("something")list.filter(10)list.sort(true); It would allow us to specify the types within Function Chaining. For example, it would be possible to have x: = fetch("something")mytype.add(10)vector.size(); IMO that's useful. Optionally it's a possibility to consider for Cpp2. |
Also type composition (derived units) are possible with A: type = { /*declarations*/ }
B: type = { /*declarations*/ }
two: = 2;
i: = 2A;
j: = 2<A*B>;
k: = (two)<A*B>;
m: = (1, 2)A;
n: = (1, 2)<A*B>;
x: A = (1, 2);
y: <A*B> = (1, 2);
// <T> is template parameter.
r: <T> A = (1, 2);
s: <T> <A*B> = (1, 2); |
I have to correct my suggestion about derived units e.g.
For arithmetic, logical and assignment operators, the type of expression is always known from themselves. So |
Comparison with other suggestions and Cpp1-styleTLDR; vs
|
Use CasesConsider the syntax of object creation is 1. LiteralsAs described in the suggestion, every object construction with a: = (2)int;
b: = ("text")string; Optionally the parentheses may be omitted for literals (e.g. So it would eliminate the need of built-in literal suffixes and prefixes. 2. Object Construction within Operator ChainingIt would be possible to construct objects within operator chaining (member access operators, unary postfix operators, level: type = { /*...*/ }
something: type = { /*...*/ }
x: = (2++)level++.member(true)something()++; Also 3. User-defined OperatorsIf add: type = {
data: int;
operator=: (out this, arg: int) = {
data = arg;
}
operator(): (this, value: int) -> int = data + value;
}
a: = 2;
b: = 2;
// x == 4
x: = (a) add (b); It looks like we have defined operator 4. User-defined Language ConstructsIf Cpp2 would support object construction chaining with list: vector<int> = /*...*/;
(copy i: int = 0) @forr (i < 10) nextt (i++) within (list) call (item) { /*...*/ } So literally EDIT: I will create a new issue (suggestion) for it EDIT: The issue is created here. Conclusion
Also EDIT: I've to clarify call(name: = "Someone", age: int /*explicit type*/ = 30); |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
1. Preface
Literal suffixes are syntactic sugars to constructors. Considering Unified Function Call Syntax for Member Functions and Non-member Functions, this suggestion is somehow something similar for Literal Suffixes and Constructors.
Briefly, I suggest to support this:
I have to explain:
ul
ands8
are type aliases in1'000ul
and"text"s8.size()
respectively.(2.5litre)water
creates a variable fromlitre
s constructor, and passes it towater
s constructor.(name, age)my::player
creates a variable from typeplayer
with(name, age)
constructor.()m4gun
creates a variable from typem4gun
with default constructor.2. Suggestion Detail
Currently, Cpp2 doesn't have a special syntax to directly call constructors. I suggest to directly call Constructors of a type in the form of Literal Suffixes. Let's name it Direct Object Construction Syntax or Constructor Call Syntax.
So
...TYPE
will be a syntactic sugar to(: TYPE = ...)
in Cpp2. For example:It requires to remove all built-in literal prefixes and suffixes:
l
,ll
,ul
,ull
suffixes from integer literals.f
,l
,f8
,f16
,f32
,f64
suffixes from floating-point literals.u
,U
,u8
,u16
,u32
prefixes from both character and string literals.R
and$
prefixes from string literals as described in this issue, will make all literals to be consistent.Constructors and UDLs (user-defined language literals) are two ways in Cpp1 to create objects from literals:
The following expression in Cpp2 satisfies the purpose of both two lines above:
These are some notes to consider:
()
. For example:If Cpp2 would have array literals as described in this issue, a similar syntax would be available to call the constructor for them. For example in a consistent way with other literals, parentheses around
[...]
aren't necessary:Consider how
...TYPE
is expressive and more readable than(: TYPE = ...)
, that's the reason why Cpp1 has UDLs. For example:It's possible to consume Cpp1 UDLs. For example:
Constructors can replace Cpp1 UDLs completely, but Cpp2 can still support to author UDLs (user-defined literal suffixes, e.g.
operator""suffix
). Probably the plan is to only consume UDLs as described in this comment from @hsutter.3. Your Questions
Will your feature suggestion eliminate X% of security vulnerabilities of a given kind in current C++ code?
No.
Will your feature suggestion automate or eliminate X% of current C++ guidance literature?
Yes, because this change makes Cpp2 to reduce the concept count with a general language feature. So it will be simpler to learn and understand which leads to smaller guidance literature.
...TYPE
, parentheses are not necessary when...
is only one literal.()TYPE
, it calls the default constructor(args...)TYPE
FUNCTION()
, it calls a function without argumentsFUNCTION(args...)
obj.FUNCTION()
obj.FUNCTION(args...)
using
statement before they can be applied to literals._
, thus they will be distinguished from UDLs which are declared in the Cpp1 standard library.4. More Examples
By declaring type aliases to have familiar names:
The process of object constructions will be simpler and readable:
5. Considered Alternatives
This suggestion is a simpler and generalized alternative way to both this issue and this issue, with a different approach. This suggestion completely unifies literal suffixes with constructors instead of integrating them with templates.
Edits
The text was updated successfully, but these errors were encountered: