I'd like to conditionally enable operator <=> overloads in my code depending on whether or not it is supported given the current version of the compiler and its command line options. For example, I'd like the following code to compile as C++14, 17, and 20 (this is essentially a sequel to this solution to a question I asked earlier):
#define SPACESHIP_OPERATOR_IS_SUPPORTED 1 // <--- i want this to be automatic
#if SPACESHIP_OPERATOR_IS_SUPPORTED
#include <compare>
#endif
template <int N> struct thing {
// assume an implicit conversion to a "math-able" type exists:
operator int () const { return 0; }
// define a set of comparison operators for same N on rhs:
bool operator == (const thing<N> &) const { return true; }
bool operator != (const thing<N> &) const { return false; }
bool operator < (const thing<N> &) const { return false; }
bool operator > (const thing<N> &) const { return false; }
bool operator <= (const thing<N> &) const { return true; }
bool operator >= (const thing<N> &) const { return true; }
int operator - (const thing<N> &) const { return 0; }
// but explicitly delete ops for different N:
// (see https://stackguides.com/questions/65468069)
template <int R> bool operator == (const thing<R> &) const = delete;
template <int R> bool operator != (const thing<R> &) const = delete;
template <int R> bool operator < (const thing<R> &) const = delete;
template <int R> bool operator > (const thing<R> &) const = delete;
template <int R> bool operator <= (const thing<R> &) const = delete;
template <int R> bool operator >= (const thing<R> &) const = delete;
template <int R> int operator - (const thing<R> &) const = delete;
// but if i don't delete <=> for differing template parameters then things
// like thing<0>() <=> thing<1>() will be allowed to compile because they'll
// be implicitly converted to an int. so i *have* to delete it when supported.
#if SPACESHIP_OPERATOR_IS_SUPPORTED
std::strong_ordering operator <=> (const thing<N> &) const = default;
template <int R> std::strong_ordering operator <=> (const thing<R> &) const = delete;
#endif
};
int main () {
thing<0> t0;
thing<1> t1;
(void)(t0 == t0); // line 39
//(void)(t0 == t1); // line 40
#if SPACESHIP_OPERATOR_IS_SUPPORTED
(void)(t0 <=> t0); // line 42
//(void)(t0 <=> t1); // line 43
#endif
}
So, first a quick explanation of that:
- The implicit
operator intis a requirement. - Comparison operators are only defined for
thing<int N>s with the sameN. - The operators for mismatched
Ns must be explicitly deleted, else the compiler will decide to implicitly applyoperator intto both sides and use theintcomparison instead (see linked question). - The intended behavior is for lines 40 and 43 (marked) to fail to compile.
Now, the reason I (think) I need to conditionally check for operator <=> support is:
- The code needs to compile as C++14, 17, and 20.
- If I don't overload
<=>at all, then things likething<0>() <=> thing<1>()are incorrectly allowed to compile (due to implicit conversion toint; same situation as the other operators). In other words: The defaultoperator <=>is not appropriate in all cases, so I can't just let it be. - If I always write both
<=>overloads, then the program fails to compile as C++14 and C++17, or presumably on compilers with incomplete C++20 implementations (although I have not encountered this).
The code above meets all of the requirements as long as I manually set SPACESHIP_OPERATOR_IS_SUPPORTED, but I want that to be automatic.
So, my question is: Is there a way to detect, at compile time, support for operator <=>, and conditionally enable code if it is present? Or is there some other way to make this work for C++14 thru 20?
I'm in a precompiler mindset but if there's some magic template solution, that works, too. I'd really like a compiler-independent solution, but at minimum I want this to work on GCC (5.x and higher) and MSVC (ideally 2015 and higher).