-
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] Allow use of named operators on non-class types #430
Comments
A library-based solution can read much nicer (https://cpp2.godbolt.org/z/13Gq8Gn4q):
|
C++ already has a way to spell operators as methods. And it already works in both C++ and Cpp2 - just inconsistently. Closing this inconsistency gap decreases the total conceptual footprint of the language, not increases it. It might increase cppfront line count, but that is not what counts. |
I think this inconsistently should be fixed if there isn't any good reason behind it by either one of the following ways:
Less inconsistency leads to less pages for the language specification. |
Can we lose the operator keyword entirely somehow?
On 20 May 2023 12:27:49 Sadeq ***@***.***> wrote:
I think this inconsistently should be fixed if there isn't any reason behind it by either one of the following ways:
* Allow to spell operators as methods on built-in types (e.g. allow 5.operator+(10)).
* Disallow to spell operators as methods on user-defined types (e.g. disallow a.operator+(b)).
Less inconsistency leads to less pages for the language specification.
—
Reply to this email directly, view it on GitHub<#430 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQKCWEUMZUMS24AT7O3XHCTC3ANCNFSM6AAAAAAXY3WOIA>.
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
Good Idea. Yes. One possible option is to use For example, operator overloading inside type definition (member functions): Xyz: type = {}
Abc: type = {
// Unary Prefix Operator Plus
// operator+: (this) -> Abc = {}
this+: (this) -> Abc = {}
// Binary Operator Plus
// operator+: (this, that: Xyz) -> Abc = {}
this+: (this, that: Xyz) -> Abc = {}
} For example, operator overloading outside type definition (non-member functions): Xyz: type = {}
Abc: type = {}
// Unary Prefix Operator Plus
// operator+: (a: Abc) -> Abc = {}
this+: (a: Abc) -> Abc = {}
// Binary Operator Plus
// operator+: (a: Abc, b: Xyz) -> Abc = {}
this+: (a: Abc, b: Xyz) -> Abc = {} In this way, if |
Hmmm, if a keyword is required, operator is more expressive than this, I was wondering why the word operator was needed at all, but maybe for parsing reasons?
On 20 May 2023 14:08:54 Sadeq ***@***.***> wrote:
Good Idea. Yes. One possible option is to use this instead of operator.
For example, operator overloading inside type definition (member functions):
Xyz: type = {}
Abc: type = {
// Unary Prefix Operator Plus
// operator+: (this) -> Abc = {}
this+: (this) -> Abc = {}
// Binary Operator Plus
// operator+: (this, that: Xyz) -> Abc = {}
this+: (this, that: Xyz) -> Abc = {}
}
For example, operator overloading outside type definition (non-member functions):
Xyz: type = {}
Abc: type = {}
// Unary Prefix Operator Plus
// operator+: (this) -> Abc = {}
this+: (a: Abc) -> Abc = {}
// Binary Operator Plus
// operator+: (this, that: Xyz) -> Abc = {}
this+: (a: Abc, b: Xyz) -> Abc = {}
In this way, if this is used within the name of declaration (e.g. the left side of :), it would be treated as a keyword similar to operator.
—
Reply to this email directly, view it on GitHub<#430 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQKFUC3X52E4WMCVW5TXHC653ANCNFSM6AAAAAAXY3WOIA>.
You are receiving this because you commented.Message ID: ***@***.***>
|
By the way, For example, operator overloading inside type definition (member functions): Xyz: type = {}
Abc: type = {
type+: (this) -> Abc = {}
type+: (this, that: Xyz) -> Abc = {}
} For example, operator overloading outside type definition (non-member functions): Xyz: type = {}
Abc: type = {}
type+: (a: Abc) -> Abc = {}
type+: (a: Abc, b: Xyz) -> Abc = {} And you're right. |
Why? Keywords can aid readability. Avoiding or reducing keywords should not be a goal by itself without other demonstrable benefits. “How do we make this as different as possible from C++1” is also a non-goal. |
All true points, i was just wondering if it was necessary, whether we could just allow function names to just be symbols, but thinking further I suppose operators really are special because they can be called without . or ()
On 20 May 2023 14:37:18 orent ***@***.***> wrote:
Can we lose the operator keyword entirely somehow?
Why? Keywords can aid readability. Avoiding or reducing keywords should not be a goal by itself without other demonstrable benefits.
“How do we make this as different as possible from C++1” is also a non-goal.
—
Reply to this email directly, view it on GitHub<#430 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQL6MYAOMVN4MVLEWYDXHDCIJANCNFSM6AAAAAAXY3WOIA>.
You are receiving this because you commented.Message ID: ***@***.***>
|
I think it's because they're not functions. Note that C++20 has this for UDTs, too. Also, I don't think we should take away the possibility of naming those functions |
I've emphased two parts from your comment. IMO that's the inconsistency I'm talking about, either they are functions, or they are not. BTW they are operators which works on both built-in types and UDTs. |
Floating point division is a function with an address, on NVidia GPUs. |
Not from the perspective of the C++ abstract machine. |
Fair. It wouldn't be a breaking change to make them addressable, given that current C++ code cannot take those addresses. It's a bit of a fiction to present built-in functions like those the same as user-defined functions. But it can be convenient for the user. Analogously to how presenting built-in types as user-defined types is convenient when it comes to UFCS. |
This depends on the view of Cpp2. Is it higher-level language than Cpp1? Does its syntax want to hide the complexity of low-level semantics? How much of low-level semantics have to be manipulated by the programmer?
It seems the aim of Cpp2 is to be safer, simpler and higher-level than Cpp1. So if built-in types are like user-defined types with UFCS in Cpp2, why not support function-style operator calls on built-in types? Cpp2 can just disallow to get the address of built-in type's operators, or make it an implementation-defined behaviour within unsafe code with a warning about it. |
Built-in type's operators are something similar to |
Note that the motivation for my original suggestion was not about fixing an inconsistency per se. It was inspired by playing around with left-to-right expressions and seeing how infix operators break them. Left to right evaluation makes long expressions easier to read, write and debug. My preferred approach is to make them global template functions. For user defined types the operator method and the global function invoked as UFCS would have exactly the same effect so you need not care which one takes precedence. This also makes the whole issue of taking their address moot. |
If we can get the address of it, doesn't it have the overhead of function call? |
It'd probably be inlined everywhere. It'd also let us not have to document and explain all this family of things: https://en.cppreference.com/w/cpp/utility/functional/less |
So we would get the address of a function which would not be called (it would be inlined everywhere). |
Exactly like any other function that's inlined in places the compiler deems appropriate. The point of getting the address to a function is not to "have an address that other people are calling". It's to call that function via de-referencing the address. If you don't call it, then the program counter might never store that value, right. |
I'm thinking about your comment. Isn't it better to just return the address of actual function? Cpp2 can wrap the operation in an inline function if they don't have an actual function. |
Yes I'm presuming the simplest implementation is a bunch of inline top-level-namespace functions like @orent proposed. If you want to call it with a stack frame and all and take its address you can, but otherwise the compiler can inline if it's a built-in of the platform. Kind of like boxed types in Java or C#. |
Being able to inline has nothing to do with them being “built in”. Compilers inline your functions while also generating a callable version if you take their address. And in a wonderful collusion with linkers they ensure different modules will get the same same function address even if they independently include the same header or otherwise generate an identical version. |
I know, but you definitely want all the arithmetic operators that are built ins of the platform to be inlined, or you'll kill performance. That's how you should read that comment. |
UFCS is force-inlined, so I think it's reasonable to expect the same here. |
I may have an use case for this, for a sufficiently transparent implementation of the feature. I'm trying to convert the Cpp1 requirement How I wish Boost.Lambda2's operators were SFINAE friendly. |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
Expression that flow from left to right without evaluation order jumping around are easier to read and write and less error prone. This is probably one of the reasons for the success of the shell pipe. The use of postfix operators in Cpp2 makes it easier to maintain such strict left-to-right order without requiring the use of extraneous parentheses. It also combines particularly well with UFCS.
But this does not work with the infix operators. While compatibility with math notation is important, having an alternative way to perform these operations without triggering the usual operator evaluation precedence rules would be useful.
The most natural alternative spelling for the infix operators is
.operator★()
. This actually already works in C++ and therefore in Cpp2. It is possible to rewriteg(f(a)+b)
as the pure left-to-rightf(a).operator+(b).g()
- but it only works when the objects are class types. This does not work with ints, for example.Suggestion:
a.operator★(b)
always equivalent to((a) ★ (b))
Possible implementations:
.operator★()
methods to the builtin non-class typesoperator★()
global builtin functions that can be used either in the formoperator★(a,b)
or UFCSed toa.operator★(b)
The latter seems simpler and would just require translating
operator★(a,b)
to standard template functionscpp2::operator_name(a,b)
.The text was updated successfully, but these errors were encountered: