/* Lecture code 16.2
 *
 * The Curiously Recurring Template Pattern (CRTP).  This code illustrates
 * how to define all six relational operators in terms of < using inheritance.
 */

#include <iostream>
#include <string>
using namespace std;

/* The class Operators is parametrized over the type that will be deriving from
 * it.  Consequently, we know at compile-time that any instance of Operators<T>
 * must also be an object of type T, and can typecast the this pointer of the
 * Operators class to be a pointer to an object of type T.
 */
template <typename DerivedType> class Operators
{
public:
	/* Define five relational operators in terms of <.  Note that we have to
	 * call downcast() to compare the current object against the other object,
	 * since currently we are an object of type Operators<DerivedType>, which
	 * does not have < defined.  See below for downcast().
	 */
	bool operator > (const DerivedType& other) const
	{
		return other < downcast();
	}
	bool operator >= (const DerivedType& other) const
	{
		return !(downcast() < other);
	}
	bool operator <= (const DerivedType& other) const
	{
		return !(other < downcast());
	}
	bool operator == (const DerivedType& other) const
	{
		return !(other < downcast() || downcast() < other);
	}
	bool operator != (const DerivedType& other) const
	{
		return other < downcast() || downcast() < other;
	}
private:
	/* Since any instance of Operators<T> must also be an object of type T,
	 * we can use a static_cast to downcast the this pointer to an object of
	 * the derived type.  downcast() performs this conversion and returns a
	 * reference to an object of the derived type.
	 */
	const DerivedType& downcast() const
	{
		return *static_cast<const DerivedType*>(this);
	}
};

/* Template class MyPair illustrating how to use the Operators class.
 * Note that MyPair inherits from Operators<MyPair<First, Second> >.
 */
template <typename First, typename Second>
struct MyPair: public Operators<MyPair<First, Second> >
{
	/* Data members. */
	First first;
	Second second;

	/* Convenience constructors. */
	MyPair() {};
	MyPair(const First& one, const Second& two) : first(one), second(two) {}

	/* Comparison function */
	bool operator < (const MyPair& other) const
	{
		if(first < other.first) return true;
		if(other.first < first) return false;

		return second < other.second;
	}
};

int main()
{
	MyPair<int, string> one(137, "C++"), two(271, "STL");

	cout << boolalpha << (one < two) << endl;
	cout << boolalpha << (one <= two) << endl;
	cout << boolalpha << (one != two) << endl;
	cout << boolalpha << (one == two) << endl;
	cout << boolalpha << (one >= two) << endl;
	cout << boolalpha << (one > two) << endl;
	return 0;
}