1

I am in the process of converting a large set of geospatial code from one projection to another. To enforce proper units during this conversion, I have introduced Distance, Point, Rectangle, and Polygon templates which take a tag indicating what coordinate system is used. This is working out fairly well, but there are a lot of places where checks are performed for non-zero (!= 0) or positive values (> 0). I would like to be able to overload these operators to allow comparing against 0 without comparing against any other number. Is it possible to accomplish this?

As an additional restriction, I cannot use constexpr because I must support VS 2013, but I'd still be interested to hear if there is a way to do this with constexpr

Just for reference, I am working with something like this:

template<typename Tag> struct Distance
{
  int value;
};

template<typename Tag> struct Point
{
  Distance<Tag> x;
  Distance<Tag> y;
};

// This works fine for comparing two Distances
template<typename Tag> bool operator>(const Distance<Tag>& a, const Distance<Tag>& b) {return a.value > b.value;}
// But I don't want this to allow a > 14, only a > 0
template<typename Tag> bool operator>(const Distance<Tag>& a, int b) {return a.value > b;}

struct Mercator;
typedef Point<Mercator> MercPoint;
struct GuiScale;
typedef Point<GuiScale> GuiPoint;
// etc.
Dark Falcon
  • 43,592
  • 5
  • 83
  • 98
  • 1
    Why not have bool functions such as `IsZero`? In my opinion overloading mathematical operators which only take 1 allowed value is a bad idea. – Neil Kirk Nov 07 '14 at 15:59
  • What's the point of `Distance` class template ? – P0W Nov 07 '14 at 16:00
  • @P0W, So you can't give two unrelated distances to `operator>` (or another function) and to give the integer type a name that corresponds to its use. – chris Nov 07 '14 at 16:02
  • Checkout these : http://stackoverflow.com/q/5556466/1870232 and http://stackoverflow.com/q/3696326/1870232 – P0W Nov 07 '14 at 16:04
  • Are these types part of an intermediate conversion step, or are they meant to be left in the final form? If the code is going to keep these types I'd refactor into named functions (`.isZero()`, `isPositive()`)... but this could be more work if the types are just temporary – David Rodríguez - dribeas Nov 07 '14 at 16:45
  • @DavidRodríguez-dribeas: They are permanent, but I never have liked having to remember `isNonzero` as opposed to `!= 0`. That was my fallback plan if nothing else worked, however. That way would also mean I absolutely have to touch every usage too, which is a pain for this multiple hundreds of thousands of line project. – Dark Falcon Nov 07 '14 at 18:10

3 Answers3

2

You may use nullptr_t as a hack (as literal 0 convert to nullptr):

template<typename Tag> bool operator>(const Distance<Tag>& a, std::nullptr_t b) {return a.value > 0;}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
2

You might use the conversion-to-any-pointer of a literal zero:

#include <type_traits>

template<typename Tag>
struct Distance
{
private:
    using literal_zero = void(Distance::*)();

    template<typename U>
    using enable_nullptr = typename std::enable_if<
        std::is_same< typename std::decay< U >::type, std::nullptr_t >::value
    >::type;

public:
    int value;

    bool operator<( literal_zero ) const { return value < 0; }

    template<typename U>
    enable_nullptr<U> operator<( const U& ) const = delete;
};

int main() {
    Distance<int> a;
    a.value = 0;

    a < 0;
    // a < 42; // does not compile
    // a < nullptr; // does not compile
}

This, unlike the other answer, also disallows a < nullptr. Also, if you remove the nullptr-related part and replace the using literal_zero = ... with a typedef, the same technique works with C++98.

Live example

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
1

If it's semantically valid to compare with a literal 0, it's valid to allow a conversion from literal 0 as well. With the conversion in place, you need no special case comparisons (Live at Coliru):

template<typename Tag> struct Distance
{
  int value;

  Distance() = default;
  explicit constexpr Distance(int v) : value(v) {}
  constexpr Distance(std::nullptr_t) : value(0) {}

  friend constexpr bool operator == (const Distance& lhs, const Distance& rhs) {
    return lhs.value == rhs.value;
  }
  friend constexpr bool operator < (const Distance& lhs, const Distance& rhs) {
    return lhs.value < rhs.value;
  }
  // ...
};
Casey
  • 41,449
  • 7
  • 95
  • 125
  • Just to be clear, [this compiles correctly in MSVC with the occurrences of `constexpr` stripped](http://rextester.com/GNLJ4961). – Casey Nov 07 '14 at 18:20