C++ Extreme Overloading

March 03, 2005

Everyday we code, we use elementary things without thinking when coding. What would happen if these things would stop working (or work different), if you cannot even rely on a simple = 5? What will you get when adding peaches to apples or multiplicating them? Mash, that's for sure in either case.

Here, I will show the details of C++ overloading and how you can use it to destroy the semantics in your code. I assume that you at least know what operator overloading is and how it works, basically. This is a list of the really weird things nobody would usually use, and of course some notes for their usage. At the end of this document is the example files.

Table of contents

General notes
List of overloadable operators
Overloading of ?:
Pointers and stuff
As an array
Type conversion
Chaining
Classes and varargs
Unoverridables
Files

General notes

Return type can be anything, if not stated otherwise. I use const char * as often as possible in overload.cpp to display a descriptive string. (MyClass was abbreviated to MCL.)

Operators can be non-static, i.e. a member of a class:

class MCL {
        const char *operator +(MX *);
}

Or they can be static, in which they can only take a class...:

const char *operator +(MX &, int) { return "operated"; }

... or an enumerated value:

const char *operator +(enum EX, int) { return "blah"; }

Failure to do so will result in either of:

`...' must be a nonstatic member function

eror: `const char* operator+(int, int)' must have an argument of class or enumerated type

Even though += 5 usually means = a + 5, these two operators combinations are different. You need a function for += too.

List of overloadable operators

Arithmetic a=b a+b a-b a*b a/b a%b
a+=b a-=b a*=b a/=b a%=b
-a ++a a++ --a a--
Logic a|b a&b a^b ~a
a||b a&&b !a
a==b a!=b a<b a>b a<=b a>=b
Pointers and stuff *a, &a, a->member
Function a(any)
Array a[b]
Type conversion static_cast<b>(a)
(b)a
Chain a,b

Especially mentioning that in C++ you can overload ,[ref] made me write this Extreme Overload overview.

No overloading of ?:

error: ISO C++ prohibits overloading operator ?:

Overloading the ?: operator is forbidden according to the G++ Compiler error message.

Pointers and stuff

int operator *(void);
int operator &(void);
classPtr operator ->(void);

The first operator listed ("unary asterisk") is for dereferencing. When you thought you can only dereference pointers, think no further — you can "*" a class and period. (Whether you still call it dereference is another matter.) What I use it for, if I use it at all, is to provide a shortcut to one very common member, such as the root node for a binary tree:

class node;
extern void analyze_node(node *);
class tree {
        node *operator *(void) { return root; }
        node *root;
};

tree *ptr = new tree(), &ref = *ptr;
analyze_node(*ref);
OR
analyze_node(**ptr);

The second one ("unary ampersand") is to "take" the address of the object. This usually means returning the "number" of the memory location where the object starts. 0xbffffe00 looks like a reasonable value. By overloading &(void) — not to be confused with the "binary ampersand", aka &(notVoid) — you effectively destroy the possibility to ever get the address of an object unless you have a pointer to it from somewhere else.

The third, and final operator for this category ("smart pointer" — no idea why it is called like that) is a real mystery. Execution control is transferred to the member function operator ->, which returns a pointer to an object (class). Since by using ->, you access a class member, the resulting class (as returned by operator ->) should have this member.

I cannot think of any real uses for operator ->, but you could count dereferencing... I do not see their name "smart pointer" being justified ATM.

As an array

const char *operator [](int);
const char *operator [](const char *);

The interesting thing here is that you can use strings as array indexers, e.g. ref[static_cast<const char *>("string")]. Note that you need a explicit cast, because [] usually takes numeral types only. Failure to provide a cast results in the following error:

error: ISO C++ says that `const char* MCL::operator[](const char*)' and operator[]' are ambiguous even though the worst conversion for the former is better than the worst conversion for the latter

Type conversion

operator int(void) { return 0xC0FFEE; }
operator const char *(void) { return "coffee"; }

MCL ref;
strcmp(a_string, ref);
if (ref == 0xC0FFEE) { ... }

Depending on in which context our class is used, it may have different return results. If the context is unclear, e.g. between the numeral types (char, short, int, long, float, double) and related pointers (char *, const char *), you will need to use casts in order to force a certain conversion. This is definitely necessary for varargs functions, see the section Classes and varargs below.

Chaining

The most extreme example is overloading the comma operator. Especially, because it interferes with a "normal" comma that is to be used for delimiting arguments in a function call. Basically, a function call takes precedence over comma. To force comma, use another pair of parentheses:

Object a, b, c, d;
// Function call with (a,b)
(a, b)(c, d);
// Two function calls (a,b) and (c,d)
(a, b)((c, d));

Classes and varargs

In the overload.cpp example file (at the end of this doc) uses more casts than usually necessary, because printf() takes variadic arguments, which are int as default type, and will be marked as ambiguous or fault by the compiler.

Passing a class in static args (line 5) works, while doing so in varargs (line 6) will generate the warning below, and a segfault at runtime.

01: void foo(MCL &, ...);
02: void bar(int, ...);
03:
04: MCL awk; 05: foo(awk, 123, 456);
06: bar(123, awk, 456);

warning: cannot pass objects of non-POD type `class MCL' through ...'; call will abort at runtime

Unoverridables

Apart from ?: where G++ clearly states it will not allow to override such, overloading ., :: or unknown operators (such as **) produces a syntax error, as will $ and any other unused symbols.

Files

Accompanying with this overview is overload.cpp which contains the MCL and MX classes to demonstrate the basics of operator overloading (including the weird ones).

Class DE exists to show a wholly mixture of operator abuse:
&(*de,1)(2)[3](-~de++^(*!de,5)==6,7)->self

Finally, there is a class FX to show the behavior of overloaded comma vs. function call.

Grab overload.cpp.