代码之家  ›  专栏  ›  技术社区  ›  Jason C

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

  •  3
  • Jason C  · 技术社区  · 4 年前

    我想有条件地启用 operator <=> 根据当前版本的编译器及其命令行选项,在我的代码中重载。例如,我希望下面的代码编译为C++ 14, 17和20(这基本上是续集) this solution 对于我之前提出的一个问题):

    #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://stackoverflow.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
    }
    

    • 含蓄的 operator int 是一个要求。
    • thing<int N> 同样的 N .
    • 不匹配的运算符 必须显式删除,否则编译器将决定隐式应用 运算符int 两边都用 int 取而代之的是比较( see linked question ).
    • 预期的行为是第40行和第43行(标记)无法编译。

    操作员<=> 支持是:

    • 代码需要编译为C++ 14, 17和20。
    • <=> 一点也不奇怪 thing<0>() <=> thing<1>() 不正确地允许编译(由于隐式转换为 ;与其他操作员的情况相同)。换句话说:默认值 不是所有情况下都合适,所以我不能就这样。
    • 如果我总是同时写 < 重载,程序无法编译为C++ 14和C++ 17,或者编译在不完全C++ 20实现的编译器上(尽管我没有遇到过这个问题)。

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

    有没有一种方法可以在编译时检测对 操作员<=> ,并有条件地启用代码(如果存在)?还是有其他方法让C++ 14通过20来实现这一点?

    我在一个预编译心态,但如果有一些神奇的模板解决方案,这也工作。我真的想要一个独立于编译器的解决方案,但至少我希望它能在GCC(5.x及更高版本)和MSVC(最好是2015年及更高版本)上运行。

    1 回复  |  直到 4 年前
        1
  •  5
  •   Barry    4 年前

    这就是特性测试宏的用途。有一个 standing document

    三方比较 明确地 有点棘手,因为这是一个需要语言和库支持的功能。有一个语言级的feature test宏,但它不是为您(用户)设计的,它是为了让标准库的作者有条件地提供该功能。

    #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
    

    你不需要提供 违约 <=> 所有关系运算符。所以我们 < :这样你就可以写作了 < 它本身。你还需要提供 operator== 但只是因为你需要做一些特别的事 delete <=>