I have written a class to represent bearings (angles with a nautical theme, and a specific normalisation range). In the program, it is necessary to perform some mathematical operations on them, so I've overloaded the +, -, * and / operators (this is in C++).
My question is: what operators are mathematically well defined for such a class? Or more specifically, in the following code, are there any operators defined that I shouldn't have defined, and are there any operators undefined that I should have defined?
constexpr inline Bearing operator+(Bearing lhs, Bearing rhs) noexcept
{
return Bearing::deg(lhs.getInDegrees() + rhs.getInDegrees());
}
constexpr inline Bearing operator-(Bearing lhs, Bearing rhs) noexcept
{
return Bearing::deg(lhs.getInDegrees() - rhs.getInDegrees());
}
template <
typename T,
typename = EnableIfNumeric<T>
> constexpr inline Bearing operator*(Bearing lhs, T rhs) noexcept
{
return Bearing::deg(lhs.getInDegrees() * static_cast<Bearing::ValueType>(rhs));
}
template <
typename T,
typename = EnableIfNumeric<T>
> constexpr inline Bearing operator*(T lhs, Bearing rhs) noexcept
{
return Bearing::deg(static_cast<Bearing::ValueType>(lhs) * rhs.getInDegrees());
}
template <
typename T,
typename = EnableIfNumeric<T>
> constexpr inline Bearing operator/(Bearing lhs, T rhs) noexcept
{
return Bearing::deg(lhs.getInDegrees() / static_cast<Bearing::ValueType>(rhs));
}
template <
typename T,
typename = EnableIfNumeric<T>
> constexpr inline Bearing operator/(T lhs, Bearing rhs) noexcept; // Intentionally not defined
constexpr inline Bearing::ValueType operator/(Bearing lhs, Bearing rhs) noexcept
{
return lhs.getInDegrees() / rhs.getInDegrees();
}
// Bearing has value semantics
constexpr inline Bearing operator+=(Bearing lhs, Bearing rhs) noexcept; // Intentionally not defined
constexpr inline Bearing operator-=(Bearing lhs, Bearing rhs) noexcept; // Intentionally not defined
constexpr inline Bearing operator*=(Bearing lhs, Bearing rhs) noexcept; // Intentionally not defined
constexpr inline Bearing operator/=(Bearing lhs, Bearing rhs) noexcept; // Intentionally not defined
constexpr inline bool operator==(Bearing lhs, Bearing rhs) noexcept
{
return lhs.getInDegrees() == rhs.getInDegrees();
}
constexpr inline bool operator!=(Bearing lhs, Bearing rhs) noexcept
{
return !(lhs == rhs);
}
In words:
- (not shown in above code) Bearing instances must be created from static methods Bearing::deg or Bearing::rad, so the units are explicit at initialisation
- Bearings may be added to or subtracted from other Bearings only
- Bearings may be multiplied only by numeric types (not other Bearings)
- Bearings may be divided only numeric types
- Numeric types may not be divided by Bearings
- Bearings may be divided by other Bearings, yielding a floating point value
- Bearings have equality comparison operators, but no inequality comparison operators (because angles wrap around, both a < b and b < a are true if a and b are Bearings)
- (note that there are member functions to determine the absolute and clockwise/anticlockwise distances between one bearing and another, so whatever you might want to do with inequality comparison operators should be possible)
Note that, by "normalisation", I mean wrapping the angles around so that they are always in the range [0, 360), or [-180, 180), and that this operation is only performed at the client's request, not after every operation.
P.S. I think this question is a good fit for Programmers, but if several people think it is a better fit for Code Review, then I will consider moving it there.
Aucun commentaire:
Enregistrer un commentaire