Escaping overloaded operators

The possibility of overloading just about any C++ operator and having it do something entirely different from what it was designed for, can sometimes make life pretty hard.

Here are a couple of examples: What if you wanted to take the address of an object, which had implemented an entirely different semantic for the ampersand (&) operator? Or what if somebody decided to overload the comma operator in some strange manner?

As you could have guessed, there are solutions for such scenarios.

The comma operator (operator,)

Suppose there’s some sequence object, which you would want to advance and then print:

#include <iostream>

template <typename T>
void print_seq (const T &seq) {
    seq.reset();
    for (unsigned i=0;i<seq.size();seq.advance(), ++i)
        std::cout << i << " " << seq.curr() << std::endl;
}

Everything looks peachy until now, right? Well, not exactly;

struct sequence {
        sequence &reset   ()       { return *this; }
        sequence &advance ()       { return *this; }
        size_t    size    () const { return 0;     }
        unsigned  curr    () const { return 0;     }
    private:
        void operator, (unsigned)  {  /* none. */  }
};

int main () {
    // this will complain about comma operator being private!
    sequence seq;
    print_seq(seq);
}

What we’ve witnessed just now is that writing generic functions (or libraries), like our templated ‘print_seq’ function, can be a little tricky when the unexpected strikes. Luckily, there’s a solution: we are able to invoke the ‘void()’ token to keep user-defined overloaded comma operators from running. Here’s how we should re-write the function:

template <typename T>
void print_seq (T &seq) {
    seq.reset();
    for (unsigned i=0;i<seq.size();seq.advance(), void(), ++i)
        std::cout << i << " " << seq.curr() << std::endl;
}

Using ‘void()’ we make sure that only the default comma operator gets to execute, instead of any user defined ones (in case there are any).

Now we shall discuss another operator which can be overloaded in many unexpected ways;

The ampersand operator (operator&), also known as ‘address of’

Assume the task at hand is to write a generic improvement of an equality test, such that it would first check if both operands are the same object (by comparing their addresses) and only then invoke operator== for the actual comparison:

template <typename T>
bool equal (const T &lh, const T &rh) {
    return &lh == &rh || lh == rh;
}

C++’s short-circuit evaluation mechanism guarantees that if the first comparison is successful, the second one (which is usually more expensive) will not be invoked.

Being familiar with the first example in this post, your spider senses must be going off at this point. You should be asking yourself: “What if T has an overloaded user-defined version of operator&?”. Indeed, let me illustrate such a case:

template <typename T>
struct vector_element {
    unsigned operator& () const { return idx_; }
    // .. rest of implementation ..
};

The ‘vector_element’ class represents an element of a vector. As such, its operator& simply returns its index in the vector. So if we have two such vectors, and we attempt to compare two of their elements using the proposed ‘equal’ function, the result is going to be wrong. For example:

std::vector<vector_element<unsigned> > v1, v2;
// ...
if (equal(v1[5], v2[5]))
    std::cout << "Always true, regardless of value." << std::endl;

So how could we avoid this situation, you ask?

Well, the folks at boost have implemented a boost::addressof() utility function, which I strongly suggest you use in case of need. For the sake of completeness, here’s how we would implement such a functionality ourselves:

template <class T>
inline T* addressof (T &val) {
    return reinterpret_cast<T*>(
        &const_cast<char&>(reinterpret_cast<const char&>(val)));
}

A couple of key points are worth noting here:

  • The const cast is useful since it saves us from the need of providing another overload for the const T& case.
  • This implementation is well defined and portable since we’re reinterpret-casting from T to A, and then vice versa. (A being a char type here, but we could have used a local class, or something else, just as easily).

The boost implementation has some extra minor tweaks, but is essentially the same.

With this implementation, we would be able to provide a correct version of the ‘equal’ functionality we were so eager to achieve:

template <typename T>
bool equal (const T &lh, const T &rh) {
    return addressof(lh) == addressof(rh) || lh == rh;
}

I hope that this post illustrates just how careful we must be when implementing very generic solutions, and that it is quite possible to overcome just about any obstacle – as long as we can foresee it.

6 thoughts on “Escaping overloaded operators

  1. That is a huge exaggeration about the flexibility/expressiveness of C++’s operator overloading model. C++’s operator overloading model is severely limiting. The number of operators you can overload is actually small. You cannot combine symbols to form new operators. You can not define fixity and/or precedence levels. You have to jump through hoops to achieve some of this via hacks involving expression templates compared to other languages such as Haskell.

    Basically C++ sucks for writing embedded domain-specific languages (EDSL) without jump through hoops with ugly hacks.

    1. Why would you want to do such an abomination?!?
      define precedence levels?!? listen to your self you are panicking!!

      the only operators that I need to overload is the “well understood” operators/every day using/mathematically agreed operators.

      And that’s what C++ is giving you.

      P.S. just for shits and giggles:
      http://www.mr-edd.co.uk/blog/xsmell_awesomer_xml

      1. You should read everything I wrote before replying. Go learn some Haskell and learn something about embedded domain specific languages before you come to me with your idiotic ignorance.

        I know more about C++ than you, I use it every day at work, I own copy of the ISO standard. I use boost, I know many advance techniques and idioms in C++ including what it takes to write EDSLs like the one you just linked.

        You got no fucking clue what you’re talking about.

  2. Yeah, C++ isn’t Haskell. But C++ isn’t Java neither, we can have pretty neat EDSLs, like with Boost.Spirit, and many other libraries.

  3. Nice! can you do something similar to prevent operator|| or operator&& for being overloaded? those are really dangerous. so it would be powerful if you could do something about it.

    Thanks! Love your posts :D

  4. In fact, I have managed to do something similar in order to prevent overloading logical operator (which is a really bad thing because of the undefined evaluation order of the parameters), here it is:
    http://pastebin.com/rstzTqZW

    Roman, please tell me if there is any design flow.

    Thank you again :P

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>