在编译时检查三向比较运算符支持

Checking for three-way-comparison operator support at compile time

我想在我的代码中有条件地启用 operator <=> 重载,具体取决于是否支持当前版本的编译器及其命令行选项。例如,我希望将以下代码编译为 C++14、17 和 20(这实际上是 sequel 到 我之前提出的问题):

#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 
    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
}

所以,首先快速解释一下:

现在,我(认为)需要有条件地检查 operator <=> 支持的原因是:

只要我手动设置SPACESHIP_OPERATOR_IS_SUPPORTED,上面的代码就满足了所有要求,但我希望它是自动的。

所以,我的问题是:有没有办法在编译时检测对 operator <=> 的支持,并在代码存在时有条件地启用它?或者有其他方法可以使 C++14 到 20 工作吗?

我处于预编译器的心态,但如果有一些神奇的模板解决方案,那也行。我真的很喜欢独立于编译器的解决方案,但至少我希望它能在 GCC(5.x 及更高版本)和 MSVC(理想情况下为 2015 及更高版本)上运行。

这就是功能测试宏的用途。有一个 standing document 定义了所有宏及其值;这些是您检查的宏和值,所有供应商都同意遵守。

三向比较具体来说有点棘手,因为这是一个需要语言和库支持的特性。有一个语言级别的功能测试宏,但它不是为您(用户)设计的,它是为标准库作者有条件地提供该功能而设计的。

所以你真正需要做的是:

#if __has_include(<compare>)
#  include <compare>
#  if defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907
#    define SPACESHIP_OPERATOR_IS_SUPPORTED 1
#  endif
#endif

现在在您的其余代码中,您可以检查 #ifdef SPACESHIP_OPERATOR_IS_SUPPORTED 以有条件地提供 <=>:

#ifdef SPACESHIP_OPERATOR_IS_SUPPORTED
    bool operator==(const thing<N> &) const = default;
    std::strong_ordering operator<=>(const thing<N> &) const = default;

    template <int R> bool operator==(const thing<R> &) const = delete; 
    template <int R> std::strong_ordering operator<=>(const thing<R> &) const = delete;
#else
    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; }

    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; 
#endif

您不需要提供 both 默认 <=> and 所有关系运算符。这就是为什么我们有 <=>: 所以你可以自己写 <=> 。您仍然需要提供 operator== 但只是因为您正在做一些需要 delete <=>.

的特殊事情