Easier comparison operators with std::tie

Being able to arbitrarily ‘look up’ or fetch an element from a collection is a critical tool in almost all C++ applications – or any programming language, for that matter.  Typically, in C++, this common paradigm manifests through the use of an std::map<TKey, TItem>  or an std::unordered_map<TKey, TItem> .  When your lookup ‘key’ is a simple, built-in type – such as an enum, int or string – everything just ‘works,’ because those simple types have built-in comparison operators.

Trivial keys can have limited usefulness, however.  More often than not, we need more complex lookup keys, in the form of aggregate types.  This post describes some tricks you can use to easily create aggregate types and reduce boilerplate code.

Aggregate types are comprised of some combination of one or more simple types, which together form a unique identifier.  Examples of aggregate keys might include:

  • Date – a structure containing year/month/day integer variables
  • Point – a structure containing an X/Y coordinate pair
  • FullName – a structure containing a first name and last name string pair.

Now; if we want to use an aggregate key in an std::map<TKey, TItem> , our key structure needs a certain degree of ‘smarts.’  In particular, we are required to support a “less-than’” comparison operator ( < ).  Nine times out of ten, this comparison is going to be lexicographical, meaning “compare member A, then compare member B, then compare member C, etc.”

Consider a Date structure:

If we want to ‘compare’ two instances of this structure against each other, we compare the year first, then the month, then the day – in that order – moving to the next comparison only if the previous members were equal.  That’s lexicographical comparison.

Okay, easy enough;

Done.

Now, imagine doing that for every single aggregate type you want to use in a map.  Do you think you’ll be able to avoid any accidental typos, after the 100th time you’ve typed in that pattern?  What about aggregate types with four members?  Five members?  Six members?  Each additional member adds yet another combinatorial line to that final boolean calculation.

Wouldn’t it be awesome if C++ had a built-in way to lexicographically compare a struct comprised of multiple members?

As it turns out, it does!

C++11 provides a class called std::tuple , which ‘groups’ together an arbitrary set of members.  std::tuple  also implements lexicographical comparison operators.  Additionally, C++11 provides a helper function called std::tie , which magically constructs a tuple using references to your provided members, avoiding the unintentional overhead of copying your struct members into a standalone std::tuple  instance.

So, armed with std::tie  and the knowledge that it provides comparisons for you, the implementation of your struct’s comparison operators can be shortened to this:

Cheap and easy, and done in one line of code.  Not only does this work for the ‘less-than’ operator; it works for all comparison operators.

Leave a Reply

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