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 v = 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 a += 5 usually means
a = 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 |
| Logic | a|b a&b a^b ~a |
| Pointers and stuff | *a, &a,
a->member |
| Function | a(any) |
| Array | a[b] |
| Type conversion |
static_cast<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;OR
extern void analyze_node(node *);
class tree {
node *operator *(void) { return root; }
node *root;
};
tree *ptr = new tree(), &ref = *ptr;
analyze_node(*ref);
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.