#### Tagged as

, 29 Sep 2008 CPOL
 Rate this:
Conversion, binary, unary, and comparison operators for your types.

## Introduction

Operator overloading is a powerful and underused (but often misused) feature that can help make your code much simpler and your objects more intuitive to use. Adding some simple operator overloads to your class or struct enables you to:

1. allow conversion to and from your type and other types
2. perform mathematical/logical operations on your type and itself or other types

## Conversion Operators

There are several ways of converting between types, but the most common are using implicit/explicit conversion operators.

### Implicit

Implicit means you can assign an instance of one type directly to another.

Let's create an example.

```public struct MyIntStruct
{
int m_IntValue;
private MyIntStruct(Int32 intValue)
{
m_IntValue = intValue;
}
public static implicit operator MyIntStruct(Int32 intValue)
{
return new MyIntStruct(intValue);
}
}```

Once we have this in our struct, we can create a new instance of it from our code by simply assigning it a value - no casting or new declarations are required in our code.

`MyIntStruct myIntStructInstance = 5;`

To implicitly convert our struct back to an `int`, we need to add another conversion operator:

```public static implicit operator Int32(MyIntStruct instance)
{
return instance.m_IntValue;
}```

Now, we can assign our `myIntStructInstance` directly to an `int`.

`int i = myIntStructInstance;`

We can also call any other function that will take an `int` and pass our instance directly to it. We can do this with as many types as we like.

Maybe, our struct also has a string member variable, in which case, it could be useful to be able to create an instance of it by directly assigning from a string. This struct may look like this...

```public struct MyIntStringStruct
{
int m_IntValue;
string m_StringValue;
private MyIntStringStruct(Int32 intValue)
{
m_IntValue = intValue;
m_StringValue = string.Empty; // default value
}
private MyIntStringStruct(string stringValue)
{
m_IntValue = 0; // default value
m_StringValue = stringValue;
}
public static implicit operator MyIntStringStruct(Int32 intValue)
{
return new MyIntStringStruct(intValue);
}
public static implicit operator MyIntStringStruct(string stringValue)
{
return new MyIntStringStruct(stringValue);
}
public static implicit operator Int32(MyIntStringStruct instance)
{
return instance.m_IntValue;
}
}```

... and we can create an instance by assigning an `int` value as before, or a `string`.

`MyIntStringStruct myIntStringStructInstance = "Hello World";`

Notice, I haven't created an implicit conversion operator from our struct to a `string`. Doing so can, in some situations, create ambiguity that the compiler cannot deal with. To see this, add the following:

```public static implicit operator string(MyIntStringStruct instance)
{
return instance.m_StringValue;
}```

This compiles without issue. Now, try this:

```MyIntStringStruct myIntStringStructInstance = "Hello World";
Console.WriteLine(myIntStringStructInstance); // compile fails here```

The compiler will fail with "The call is ambiguous between the following methods or properties...". The console will happily take an `int` or a `string` (and many other types, of course), so how do we expect it to know which to use? The answer is to use an explicit conversion operator.

### Explicit

Explicit means we have to explicitly perform the cast in our code. Change the keyword `implicit` to `explicit` in the last conversion operator so it looks like this.

```public static explicit operator string(MyIntStringStruct instance)
{
return instance.m_StringValue;
}```

Now, we can return the string value, but only if we explicitly cast to `string`.

```MyIntStringStruct myIntStringStructInstance = "Hello World";
Console.WriteLine((string)myIntStringStructInstance);```

We get the output we expected.

### Implicit vs. Explicit

Implicit is far more convenient than explicit as we don't have to explicitly cast in our code, but as we have seen above, there can be issues. There are other things to bear in mind, however, such as: if the objects being passed in either direction will be rounded or truncated in anyway, then you should use explicit conversion, so the person using your object isn't caught out when data is discarded.

Imagine a series of complex calculations being done with decimals, and we have allowed our struct to implicitly be converted to decimal for convenience, but the member variable where it is stored is an integer. The user would rightly expect any object that implicitly passes in or out a decimal to retain the precision, but our struct would lose all decimal places, and could cause the end result of their calculations to be significantly wrong!

If they have to explicitly cast to or from our struct, then we are effectively posting a warning sign. In this particular situation, it may be (is) better to inconvenience the user and have neither implicit nor explicit, so they have to cast to an `int` to use our struct, and then there is no misunderstanding.

There are others issues too - do some research before using `implicit`. If in doubt, use `explicit`, or don't implement the conversion operator for that type at all.

## Binary operators

Binary operators (surprisingly!) take two arguments. The following operators can be overloaded: `+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `<<`, `>>`.

Note: It's important that you don't do anything unexpected when using operators, binary or otherwise.

These operators are normally pretty logical. Let's take the first, `+`. This normally adds two arguments together.

```int a = 1;
int b = 2;
int c = a + b; // c = 3```

The `string` class, however, uses the `+` operator for concatenation.

```string x = "Hello";
string y = " World";
string z = x + y; // z = Hello World```

So, depending on your situation, you may do whatever is logically required.

Imagine a struct that holds two `int`s, and you have two instances that you wish to add. The logical thing to do would be to add the corresponding `int`s in each instance. You may also want to add just one `int` to both values. The resulting struct would look something like this.

```public struct MySize
{
int m_Width;
int m_Height;
public MySize(int width, int height)
{
m_Width = width;
m_Height = height;
}
public static MySize operator +(MySize mySizeA, MySize mySizeB)
{
return new MySize(
mySizeA.m_Width + mySizeB.m_Width,
mySizeA.m_Height + mySizeB.m_Height);
}
public static MySize operator +(MySize mySize, Int32 value)
{
return new MySize(
mySize.m_Width + value,
mySize.m_Height + value);
}
}```

It can then, obviously, be used like this:

```MySize a = new MySize(1, 2);
MySize b = new MySize(3, 4);
MySize c = a + b; // Add MySize instances
MySize d = c + 5; // Add an int```

You can do similar things for all the overloadable binary operators.

## Unary operators

Unary operators take just one argument. The following operators can be overloaded: `+`, `-`, `!`, `~`, `++`, `--`, `true`, `false`. Most types have no need to implement all these operators, so only implement the ones you need!

A simple example using `++` on the struct used above:

```public static MySize operator ++(MySize mySize)
{
mySize.m_Width++;
mySize.m_Height++;
return mySize;
}```

Note: The `++` and `--` unary operators should (for standard operations) alter the integral values of your struct, and return the same instance, and not a new instance, with the new values.

## Comparison operators

Comparison operators take two arguments. The following operators can be overloaded. [`==`, `!=`], [`<`, `>`], [`<=`, `>=`]. I have grouped these in square brackets as they must be implemented in pairs.

If using `==` and `!=`, you should also override `Equals(object o)` and `GetHashCode()`.

```public static bool operator ==(MySize mySizeA, MySize mySizeB)
{
return (mySizeA.m_Width == mySizeB.m_Width &&
mySizeA.m_Height == mySizeB.m_Height);
}
public static bool operator !=(MySize mySizeA, MySize mySizeB)
{
return !(mySizeA == mySizeB);
}
public override bool Equals(object obj)
{
if (obj is MySize)
return this == (MySize)obj;
return false;
}
public override int GetHashCode()
{
return (m_Width * m_Height).GetHashCode();
// unique hash for each area
}```

The comparison operators normally return a `bool`, although they don't have to, but remember, don't surprise the end user!

## Other operators

This just leaves the conditional operators, `&&`, `||`, and the assignment operators, `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=`. These are not overloadable, but are evaluated using the binary operators. In other words, supply the binary operators, and you get these for free!

## Conclusion

I hope this introduction to operator overloading has been useful and informative. If you haven't used them before, give them a try - they can be a useful tool in your box.

• Don't overuse them. If an operator or conversion will definitely never be needed, or it's not obvious what the result will be, don't supply it.
• Don't misuse them. Yes, you can make `++` decrement values, but you most definitely shouldn't!
• Be very careful when implicitly converting your classes or structs to other types.
• Remember, structs are value types, and classes are reference types. It can make a huge difference in how you handle things in your overload methods.

## History

• 31 August 2008: Initial version.
• 6 September 2008: Added the `==`, `!=`, `Equals`, and `GetHashCode` examples.
• 2 October 2008: Added note about returning from the `++` and `--` overloads.

## Share

United Kingdom
No Biography provided

 First Prev Next
 Index Operator Member 10309231 27-Aug-14 18:41
 Good article Donsw 23-Jan-09 11:32
 Good article, this is very good for beginers. Operator overloading has been in C++ for a long time. this is new only to people how never knew c++.
 Very good!, kurre85 31-Aug-08 22:34
 Re: Very good!, DaveyM69 1-Sep-08 0:41
 Re: Very good!, GSerjo 29-Sep-08 6:37
 Last Visit: 31-Dec-99 19:00     Last Update: 17-Dec-14 19:06 Refresh 1

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Web04 | 2.8.141216.1 | Last Updated 29 Sep 2008