====== Operator Overloading for Manufactured Types ======
By //[[:wiki:user:tatewake|Terence J. Grant]] 07/15/2006//
**Note** The results of this article so far have only been tested with Microsoft Visual Studio 2005 and may not represent the generic case (gcc.)
Operator overloading in C++ is a very useful feature. It can be used, for instance, to extend the set of primitive types available in C++ --that is, to create new, albeit man-made, "primitive" types.
Recently, I've come to rely on C++ operator overloading for this very reason. However, I have been less than successful in finding a complete list of operators to overload to accomplish this. Thus I have compiled this list.
So, without further ado, here is a brief list of C++ operators you can overload for creating manufactured types, and the method of which to do so.
===== Generally Speaking... =====
In general, the form of operator overloading is the following:
some-type-a class-name::operatorX(some-type-b other);
some-type-a class-name::operatorX(some-type-b &other);
Where **X** represents a placeholder for a valid operator. The **const** keyword will also make appearances when necessary and is typically optional. When you see **{const}**, const with curly braces, this is an indication that it is purely optional and not required.
The classes/types **some-type-a** and **some-type-b** can be any classes or types, even the same class as **class-name**. And of course you can define as many operators for as many different class types as you like.
You'll notice that there are two options-- by value and by reference. It appears that for all intents and purposes, you could use either. Most of the time however, it is best to play it safe and simply use references.
Also, I find most of the time I'll want to prefix each operator with **inline**, because again, we're talking generally about manufacturing new primitive types, and its good to hint to the compiler that we want to favor speed.
===== Construction and Assignment =====
Let's first mention that constructors are important. Constructors are not operators per se-- they do go hand in hand with cast operators though, so it is important to mention the list of constructors to consider.
class-name::class-name();
{explicit} class-name::class-name(const class-type &other);
{explicit} class-name::class-name(const some-type-a &other);
{explicit} class-name::class-name(const some-type-b other);
{explicit} class-name::class-name(const some-type-c other);
For the parameterless constructor, typically you'll just leave it as an empty method, to mimic what happens when you don't initialize a primtive type.
For constructors with parameters, you'll want to typically use const so that you're not tempted to modify the initializer.
You may also wish to use the **explicit** keyword if you don't want an auto-conversion from some type to your class type. You may still //explicitly// construct your class with the other type.
Standard assignment is similar.
class-type& class-name::operator=(const some-type-b &other);
If you do not define the assignment operator, the constructor will be used to create a temporary object, and then the copy constructor will be used. (This is potentially a longer set of operations.)
===== Cast =====
During casting you will typically want to support the same types that you initialize with.
operator some-type-b();
Neither the const keyword nor the explicit keyword is supported in this case.
===== Unary Minus =====
This is the arithmetic negation operation.
{const} some-type-a class-name::operator-() const;
===== Arithmetic Binary Operators =====
Here are the standard arithmetic binary operators.
{const} some-type-a class-name::operator+(const some-type-b &other) const;
{const} some-type-a class-name::operator-(const some-type-b &other) const;
{const} some-type-a class-name::operator*(const some-type-b &other) const;
{const} some-type-a class-name::operator/(const some-type-b &other) const;
{const} some-type-a class-name::operator%(const some-type-b &other) const;
===== Arithmetic Assignment =====
Notice that you return a reference. The reference you are to return is **this**.
class-type& class-name::operator+=(const some-type-b &other);
class-type& class-name::operator-=(const some-type-b &other);
class-type& class-name::operator*=(const some-type-b &other);
class-type& class-name::operator/=(const some-type-b &other);
class-type& class-name::operator%=(const some-type-b &other);
===== Unary Binary Negation =====
This is the binary negation operation.
{const} some-type-a class-name::operator~() const;
===== Bitwise Binary Operators =====
Here are the standard arithmetic binary operators.
{const} some-type-a class-name::operator&(const some-type-b &other) const;
{const} some-type-a class-name::operator|(const some-type-b &other) const;
{const} some-type-a class-name::operator^(const some-type-b &other) const;
{const} some-type-a class-name::operator<<(const some-type-b &other) const;
{const} some-type-a class-name::operator>>(const some-type-b &other) const;
===== Bitwise Assignment =====
Notice that you return a reference. The reference you are to return is **this**.
class-type& class-name::operator&=(const some-type-b &other);
class-type& class-name::operator|=(const some-type-b &other);
class-type& class-name::operator^=(const some-type-b &other);
class-type& class-name::operator<<=(const some-type-b &other);
class-type& class-name::operator>>=(const some-type-b &other);
===== Increment and Decrement =====
Notice that you return a reference. The reference you are to return is **this**.
class-type& class-name::operator++(); //prefix: ++this
class-type& class-name::operator--(); //prefix: --this
const class-type class-name::operator++(int); //postfix: this++
const class-type class-name::operator--(int); //postfix: this--
For postfix operators, save a temporary copy of this, perform the increment/decrement on this, but return the older version.
===== Comparison Operators =====
Here is a list of all relational and logical comparisons.
const bool class-name::operator==(const some-type-a &other);
const bool class-name::operator!=(const some-type-a &other);
const bool class-name::operator<=(const some-type-a &other);
const bool class-name::operator>=(const some-type-a &other);
const bool class-name::operator<(const some-type-a &other);
const bool class-name::operator>(const some-type-a &other);
===== Friend Binary Operators =====
Typically you'll want to keep your internal representation of a type protected, but you'll want to do operations like int + type as well as type + int. Though int + type is not supported above. So in these kind of cases, you'll need to invoke a "friend"-based operator **function**.
Operator functions exist outside of classes, and can be used to define operations on two different(or similar) types at any time.
So for example, let's say you want to overload operator+ for both int + mytype, and mytype + int. You'll need to do the following:
First, place this within your class declaration:
friend const some-type-a operator+(const mytype &a, const int &b);
friend const some-type-a operator+(const int &b, const mytype &a);
Then outside of the class, define the functions as needed:
const some-type-a operator+(const mytype &a, const int &b);
const some-type-a operator+(const int &b, const mytype &a);
And that pretty much covers all the operations you'll want to use on manufactured types.