代码之家  ›  专栏  ›  技术社区  ›  rbv

如何使用std::string成员的默认太空船运算符

  •  1
  • rbv  · 技术社区  · 1 年前

    我有一个结构,它有几个POD类型和一个字符串成员。我想使用默认的太空船操作符来允许对我的结构进行相等操作,但字符串成员有一些问题。据我所知,应该有人支持 <=> 对于 std::string ,但在实践中,情况似乎并非如此。

    我有一个最小的复制示例,以及从下面的clang(版本14)中得到的警告。奇怪的是,在godbolt中,这个例子没有产生任何警告/错误,甚至可以追溯到clang12( https://godbolt.org/z/b65s9oMGf )。

    我真的很感激能为我的误解提供一些线索,因为我对这一点很困惑。

    #include <compare>
    #include <string>
    
    enum class MyEnum
    {
        ZERO = 0,
        ONE = 1
    };
    
    struct MyStruct
    {
        float a{};
        int b{};
        std::string c{};
        MyEnum d{};
    
        auto operator<=>(const MyStruct&) const = default;
    };
    
    int main()
    {
        MyStruct my_struct;
        MyStruct my_other_struct;
    
        if(my_struct == my_other_struct)
        {
            return 0;
        }
    
        return 1;
    }
    
    $ clang --std=c++20 -Werror test.cpp                                                                                                                                                                                      
    test.cpp:16:10: error: explicitly defaulted three-way comparison operator is implicitly deleted [-Werror,-Wdefaulted-function-deleted]
        auto operator<=>(const MyStruct&) const = default;
             ^
    test.cpp:13:17: note: defaulted 'operator<=>' is implicitly deleted because there is no viable three-way comparison function for member 'c'
        std::string c{};
                    ^
    1 error generated.
    $ clang --version                                                                                                                                                                                                         
    Apple clang version 14.0.0 (clang-1400.0.29.202)
    
    0 回复  |  直到 1 年前
        1
  •  4
  •   303    1 年前

    根据评论,这个版本的clang及其标准库似乎:

    • 确实实现了的语言功能 <=> 但是
    • 尚未实现的库添加 <=> (特别添加 <=> 对于 std::string )。

    当你写作时:

    struct MyStruct
    {
        float a{};
        int b{};
        std::string c{};
        MyEnum d{};
    
        auto operator<=>(const MyStruct&) const = default;
    };
    

    什么违约 <=> 方法是使用 <=> ,返回比较不相等的第一个成员的值。当您使用 auto ,即要求编译器为您推导比较类别,但与的典型用法不同 汽车 (其中所有返回类型必须相同,以及 汽车 推导到那一种类型),这里我们取所有比较类别的最小值。例如,如果我们有一个成员 <=> 返回 weak_ordering 但另一个 <=> 返回 strong_ordering ,a默认 <=> 会回来的 弱排序 (而不是不编译)。基本上,它按照你期望的方式,做你可能希望它做的事情。

    但是——有时违约 <=> 如果你的一个成员实际上没有提供 <=> 然而 (在这种情况下)。在这种情况下,必须手动写出所有的比较结果会很乏味。因此,有另一种方法可以默认 <=> (来自 P1186 ):您可以显式提供比较类别:

    struct MyStruct
    {
        float a{};
        int b{};
        std::string c{};
        MyEnum d{};
    
        std::strong_ordering operator<=>(const MyStruct&) const = default;
    };
    

    这意味着,而不是简单地做会员 <=> ,我们做了一些更为复杂的事情:

    • 如果该成员 <=> ,使用它。并且它必须满足比较类别的要求(在这种情况下,它将碰巧失败,因为 float s与相比 partial_ordering ,不符合 strong订单 要求-因此必须返回 部分排序 )。
    • 如果成员 没有 提供 <=> ,我们从 == <

    因此,在这种情况下:

    struct MyStruct
    {
        float a{};
        int b{};
        std::string c{};
        MyEnum d{};
    
        std::partial_ordering operator<=>(const MyStruct&) const = default;
    };
    

    会表现得像:

    std::partial_ordering operator<=>(const MyStruct& rhs) const {
        // a and b provide <=>, so use it
        if (auto cmp = a <=> rhs.a; cmp != 0) return cmp;
        if (auto cmp = b <=> rhs.b; cmp != 0) return cmp;
    
        // c doesn't yet, so synthesize one
        if (auto cmp =
            (c == rhs.c ? partial_ordering::equivalent : 
             c < rhs.c  ? partial_ordering::less :
             c > rhs.c  ? partial_ordering::greater :
                          partial_ordering::unordered); cmp != 0) return cmp;
    
        // and d does, so use it
        return d <=> rhs.d;
    }
    

    这个语言功能的优点(正如我在那篇论文中所阐述的)是 std::字符串 确实提供 <=> ,您的实现将自动获取它,并且效率更高(因为上面的实现对于字符串来说真的不是一个好的实现,您希望这样做 c.compare(rhs.c) <=> 0 ),而不用你自己做所有的工作。