As the title says, I find it useful to be able to overload operators. Is it possible to also change the way the operators are parsed by specifying the precedence and associativity of overridden operators?
4 Answers
No, you can't do that and that's a good thing. Operator overloading already has enough potential to make code unreadable without being able to change precedence or associativity.
If you even want to do that you are probably abusing operator overloading and should use normal functions instead.
-
4-1 Your second paragraph is unnecessarily combative. Ad-hoc operators are immensely valuable for improving the clarity and concision of code through DSLs. The problem with C++ operator overloading is that it’s essentially unrestricted—so your first paragraph is absolutely on point. Aug 16, 2012 at 22:17
-
8+1 for the second paragraph. Operator precedence and associativity is built into the common language under the programming language. Operator overloading can make code clear and concise. It can also create some incredible obfuscation. Consider the fun you would have debugging something in which (a+b) != (a+b). (Bad enough that you can't rely on f(x) == f(x).) Apr 6, 2014 at 17:51
-
5@JohnR.Strohm but consider the debugging fun where a.add(b) != a.add(b). Operator overloading is a good syntactic sugar, nearly all of the arguments against it are strawmen.– gbjbaanbApr 7, 2014 at 7:37
-
@gbjbaanb, at least a.add(b) warns me that there's something hinky going on, that I have to track down. Years ago, I had to debug a piece of code, where the guy had done a quick copy-and-paste, and mistakenly wound up overloading '*' to be addition. First, you have to find where he DID it. Apr 7, 2014 at 13:31
-
that'll be the function called operator*() though, no different to a function called anything else.
a.add(b)
tells you nothing other than a function call is being made - exactly the same as if you'd used the + operator. I suppose you could use the alternative form:a.operator+(b);
if you really preferred the explicitness. So you found a bug... when I was little I was chased by a dog.. but that doesn't mean I have to be scared of dogs today.– gbjbaanbApr 7, 2014 at 13:41
No. Besides making code unreadable, it would make the language more ambiguous and deeply context-dependent because you would not be able to associate parameters with function calls until after you know all the operators available.
Consider the expression
a1 + a2 * a3
where all a*
vars have type A
and you have overloadings
A operator+(A, A) // low precedence
A operator*(A, A) // high precedence
B operator+(A, A) // high precedence
A operator*(B, A) // low precedence
This could be interpreted two different ways
operator+(a1, operator*(a2, a3))
or
operator*(operator+(a1, a2), a3)
With global precedence and associativity rules, the compiler can commit to the first interpretation during parse, but with overridable associativity/precedence, there's no way to figure out how to decompose tokens into function calls until you know all the available operator signatures.
This doesn't make the language impossible to parse (although there are more programs that have to be rejected as untypable) but it would make it slower to compile, and harder to read.
-
1In Swift you can set the priority and associativity of operators (common operators are defined in the standard library). This applies to the operator, for example "*", in all its uses. It cannot be changed per operator function. So a1 + a2 * a3 could be parsed without knowledge of the types of a1, a2, and a3. Mar 16, 2018 at 22:15
-
No. While it may at first sight seem to make sense, if you think a little more, it becomes muddy to the point that it's a bad idea.
First note that the language isn't defined by using a precedence grammar, but a more classic BNF. I'm not sure that the behavior of sizeof
or ?:
is describable simply with a notion of priority and associativity.
But the major issue is that you are changing the priority and associativity of overloaded operators, not defining the priority and associativity of new operators used only for your types. Do you want your changes to be applicable for all uses, or just for your overload? You'll probably agree that the first option is a sure receipt for trouble. The other isn't much better. How to you know it's your overload which should be considered before doing overload resolution, which need parsing and thus associativity and priority to be known.
After reading some of the answers I think that, yes, it's time for me to look for down-votes.
- Does
<<
represent bit shift withstd::ostream
? - Does
+
represent an arithmetic addition withstd::string
? - Does
*
represent dereference inboost::spirit
?
Despite this well established fact, there are still people claiming that overloading operator makes the code unreadable, and hence you should not ... blah blah ... and them must not ... blah blah ... and allowing to change precedence.
I find these arguments simply incoherent.
Operators are what makes entity to form expression that together defines an algebra. If an entity is not numeric, there is no need (I mean mathematical need) that such an algebra has to follow the same rule of natural numbers.
One of the reason that overloaded operators makes code hard to read is just because they are constrained to stay into the int semantic rules, even for things that do not have integer semantics and hence don't need that same syntax.
The answer to the question is:
NO: you cannot change precedence because the language specification doesn't allow it. Full stop.
And if you want to know why, you have to look at what the language is for.
C++ is already an complex language to parse and to translate.
By admitting "flex operators" (or even the definition of arbitrary infix-functions, like **
, <>
, <->
, etc) the expression parser cannot anymore be deployed aside of types, and that was argued by language designer as a "feature with negative tradeoff" (it cost more to be deployed that the advantage it will ever give in real world). It's just "echonomy", not "morality".
All the "moral" aspect (don't do it because it hurts me) have nothing to do with the language and the operator abuse (I can even abuse of for
and use it instead of if
, but the problem is me , not the for
keyword. bashing it makes no sense as makes no sense to bash variable precedence even without knowing a proper use of it, for the simple reason that there cannot be a use - no matter if bad or good - being such a thing prohibited at the origin.) and are just related to language marketing.
C++ operator overloading was introduced to let scientists define private algebraic types. It is normal for them to ask more flexibility in defining and using symbols.
The Java language was defined to standardize coding around OOP. And since not all programmers wannabees are also scientific researchers, Java removed a number of features that can make the language deviate from the OOP paradigm it was philosophically designed around. And to motivate that to newbies, a dogmatic religion claiming "operator considered harmful", "global variables considered harmful" and so on was formalized ad deployed across schools.
And the bash against certain coding practices perfectly common in maths and science began just from there.
It is very simple to teach "no because is bad and makes you go to hell" instead to provide and compare rational ideas. And the result - 20 years later - is that if you do it, you really go to hell because all that religion believers (that nowadays are a huge amount of people) will make your life impossible.
And since Java became the first language newbies are exposed (and C++ eventually a second one) and its paradigms taught as dogmas to students, when those students become programmers they end up applying those "dogmas" to anything by an innate "extension".
After all no-one can give a rational reason abut his favorite football team. it just happen to be when you start follow it. There are no "technical reasons". Just passion (dogmas) and the stress to change (OOP methods forced even outside OOP context)
The reason you cannot change precedence is simply that the world retains that changing that dogma will cause too much stress to too much believers. But rationally speaking, there is no reason you can believe differently and makes you define a language with more freedom in usage.
-
+1, but I have a feeling Java didn't allow operator overloading because it had limitations in its underlying format, so only methods were allowed - one reason why properties are so prevalent, you could not access the member directly, but you could via a method call. So it was there to make the platform easier to create, not because of ideology. (don't ask for a reference, this is dredged up from memory of an IBM course nearly 20 years ago!)– gbjbaanbApr 7, 2014 at 7:45
-
@gbjbaanb: yes, that's how things technically go, but how have them been sold till now? "We did not have them, because they are bad" (in fact they din't do them because they where too complicated to manage, being themselves "inter-class" (not intraclass, like methods are: C++ allows global methods...) and being natural "dual dispatch origins". Something Java don't have and C++ may have only in limited way (not that the visitor pattern is only a surrogate, in this matter: C++ dual dispatch is mostly related to template specializations) But that's a far long OT aspect. Apr 7, 2014 at 9:09
-
Not every property of an operator needs to be present in every instance of it. With
+
you have an associative binary relation with an identity element for bothint
andstd::string
– CalethMar 16, 2018 at 9:48
*
as a*
? When overloading, just set what you want to do in a high precedence operator into a*
, and set+
to those with lower precedence. Then, you don't have to worry about the priorities anymore.