Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confused about operator overloading

As I progress on my little math library I'm stumbling upon certain aspects of C# and the .NET Framework that I'm having some trouble understanding.

This time it's operator overloading and specifically the term overloading itself. Why is it called overloading? Do all objects by default have an implementation of all operators? That is, is:

public static object operator +(object o1, object o2)

predefined somewhere and somehow? If so, why then if I try o1 + o2 I get the compile time error Operator '+' cannot be applied to operands of type 'object' and 'object'? This would somehow imply that by default objects do not have predefined operators so then how come the term overloading?

I ask this, because in my math library, working with 3D geometry elements, I have the following structs: Vector and Point. Now, internally I want to allow the following construct to create vectors:

static Vector operator -(Point p1, Point p2) {...}

As this is not mathematically 100% correct but very useful internally to reduce code clutter, I don't want to expose this operator publicly, so my initial intention was to simply do:

internal static Vector operator -(Point p1, Point p2) {...}

Surprisingly (for me) I got the following compile time error: User-defined operator 'Geometry.operator +(Geometry.Vector, Geometry.Vector)' must be declared static and public".

Now this restriction that all operators must be public does seem to make some sense with the whole overloading aspect of operators but it seems that it is all kind of inconsistent:

  1. Objects by default do not have predefined operators: I can't do object + object by default or myTpye + myType without explicitly defining the + operator beforehand.
  2. Defining operators is not described as creating an operator, it's described as overloading which somehow is inconsistent (to me) with point 1.
  3. You cannot restrict access modifier of an operator, they have to be public, which does not make sense considering point 1. but sort of makes sense considering point 2.

Can somebody explain the mess I'm making of all this in simple terms?

like image 345
InBetween Avatar asked Jun 18 '13 11:06

InBetween


3 Answers

Why is it called overloading?

It's called "overloading" because it is overloading. We "overload" a thing when we give two possible implementations for that thing and then must decide which to use (which is called overload resolution).

When we overload methods we give two or more implementations of a method with a given name. When we overload operators we give two or more possible implementations for an operator with a given syntax. It's the same thing.

Make sure you're not confusing overloading with overriding. Overloading is simply the existence of two methods/operators with the same name/syntax in the same declaration space. Overriding deals with how the content of a virtual method slot is filled in at runtime.

Do all objects by default have an implementation of all operators?

No.

Is public static object operator +(object o1, object o2) predefined somewhere and somehow?

No.

This would somehow imply that by default objects do not have predefined operators so then how come the term overloading?

I don't understand the question. Of course C# has predefined operators.

I don't want to expose this operator publicly

Then don't use an operator; make a private, internal or protected method.

The design of C# is that an operator is always part of the public surface area of a type. It is very confusing to have operators that have a meaning that depends on what accessibility domain the usage occurs in. C# has been carefully designed to be a "pit of quality" language, where the choices of the language designers lead you away from writing confusing, buggy, hard-to-refactor programs. Requiring that user-defined operators be public and static is one of those subtle design points.

(1) Objects by default do not have predefined operators

Sure they do; there are hundreds of predefined operators on a variety of objects. For addition, for example, there are the following predefined overloads of operator +:

int + int
uint + uint
long + long
ulong + ulong
double + double
float + float
decimal + decimal
enum + underlying  (for any enum type)
underlying + enum
int? + int?
uint? + uint?
long? + long?
ulong? + ulong?
double? + double?
float? + float?
decimal? + decimal?
enum? + underlying?
underlying? + enum?
string + string
object + string
string + object
delegate + delegate (for any delegate type)

Consult the C# specification for a list of all the predefined overloads of all the other operators.

Note that overload resolution for operators has two phases: first, overload resolution attempts to find a user-defined overload that is the unique best; only if doing so finds no applicable candidates are the predefined overloads considered by overload resolution.

(2) Defining operators is not described as creating an operator, it's described as overloading which somehow is inconsistent (to me) with point 1.

I don't understand why you find it inconsistent, or, for that matter, what you find inconsistent. The term "overload" is used consistently to describe both operators and methods; in both cases it means to use the same syntax to refer to two or more different things, and that ambiguity is then resolved via "overload resolution". The exact details of the method and operator overload resolution algorithms are different but they are similar in the overall algorithm: first a candidate set is identified, then inapplicable candidates are removed, then a betterness algorithm eliminates applicable candidates that are worse than another, then a bestness algorithm determines the unique best candidate that is left, if any.

(3) You cannot restrict access modifier of an operator, they have to be public, which does not make sense considering point 1. but sort of makes sense considering point 2.

I don't understand what point (3) has to do with points (1) or (2) at all. The restriction that operators must be part of the public surface area is to prevent the confusing situation of being able to add a Fruit to an Animal when you're inside class Apple but not when you're inside class Giraffe.

Operators are declared inside a class or struct and therefore "belong" to said type, they don't float "belonging" to no given type. So what am I overloading exactly when I declare an operator in a class?

You're overloading the operator.

That the same operator exists between ints does not mean I am overloading anything as that operator belongs to int. To me its the same as saying the Foo.Hello() and Bar.Hello(string hello) are overloads of Hello. They are not as they are declared in two separate types. What is the difference with operators?

You've just accurately described the difference. Method overloading and operating overloading differ in many of their details.

If you want to take the position that Foo.Hello() and Bar.Hello(string) are "overloads" of Hello, that's not a common position to take but it is logically consistent.

I was under the impression that you cannot change the access modifier when overloading.

Your impression is mistaken; you cannot change access modifiers when overriding a virtual method. You've confused that with overloading.

(And I note that there is one scenario in which you are required to change the access modifier when overriding a virtual method; can you deduce what it is?)

I was also under the impression that you can not declare an operator without at least one of the operands being of the type in which you declare the operator.

That's almost correct. A user-defined operator must have an operand of type T, where T is the enclosing class or struct type, or T? if T is a struct type.

So how can a third class have access to a given operator while another third class can't unless one belongs to an external assembly and the other doesn`t in which case I do not find it confusing at all and even useful?

You've mischaracterized my example, which could have been more clear. This is illegal:

public class Fruit 
{ 
    protected static Shape operator +(Fruit f, Animal a) { ... } 
}

Because this is bizarre:

public class Apple : Fruit
{ 
    ...
    Shape shape = this + giraffe; // Legal!
}
public class Giraffe : Animal
{
    ...
    Shape shape = apple + this; // Illegal!
}

This is just one example. In general it is a strange thing to do, to make overload resolution of an operator depend on the accessibility domain, so the language designers ensured that this never happens by requiring user-defined operators to be public.

I simply find overloading confusing in the context of operators.

Many people do, including compiler writers. The user-defined operators portion of the specification is extremely difficult to parse, and the Microsoft implementation is a rich source of compiler bugs, many of which were my fault.

I don't see why simply declaring operators in a type has to described differently from how you would describe declaring any other static method.

Well, different things are different; operators are different than methods in many ways, including their overload resolution algorithms.

I have never particularly liked that C# has overloadable operators. The C# feature is a somewhat better designed version of the same feature in C++, but in both languages the feature in my opinion entails far larger costs than the corresponding user benefits.

Thank goodness at least C# does not thoroughly abuse the << operator the way idiomatic C++ does -- though of course it does abuse + and - for delegates.

like image 199
Eric Lippert Avatar answered Oct 06 '22 15:10

Eric Lippert


  1. you can't do object + object because operators must be declared in the type of one of the operands; Point + Point must be declared in Point; you can't define object + object because you are not declaring object; note that all objects have (at the language level, at least) the == / != operators
  2. the only important distinction here is that you don't call it overriding - it should not be mistaken that they are somehow polymorphic - they aren't; if you want to think of it as "creating", that's fine, but keep in mind that in non-trivial cases there can be multiple overlapping operators involving the same types, for example with the == / != operators, or where operators are inherited from a non-trivial hierarchy
  3. it is what it is; the specification says they need to be public, so either make them public, or use non-operator internal methods
like image 24
Marc Gravell Avatar answered Oct 06 '22 14:10

Marc Gravell


You are probably confusing the term "overloading" with the term "overriding".

This is an example of overloading the method Foo:

public class Parent
{
    public sealed void Foo(int n) { }
}

public class Child : Parent
{
    public sealed void Foo(string s) { }
}

This is an example of overriding the method Foo:

public class Parent
{
    public virtual int Foo() { return 0; }
}

public class Child : Parent
{
    public override int Foo() { return 1; }
}

As you can see, overloading is adding a brand new signature that will always be called independently of the previous existing signature. Overriding involved providing a brand new implementation of an existing signature.

In this case you can think of the + operator as just another method (it essentially is, beneath some different syntax). If you were providing a new implementation of an existing signature, let's say the operator + (int a, int b) signature, then you would be overriding that method. (Note that it's not possible to override an operator, or any static method, in C#.) What you're doing is overloading it by adding a new "signature" to the plus operator that takes a set of operands that didn't previously exist for any other overload of the plus operator.

like image 40
Servy Avatar answered Oct 06 '22 16:10

Servy