c++ 如何使用std::string成员的默认飞船操作符

hyrbngr7  于 2023-02-06  发布在  其他
关注(0)|答案(1)|浏览(121)

我有一个包含几个POD类型和一个字符串成员的结构体。我想使用默认的飞船操作符来允许对我的结构体进行相等操作,但我遇到了一些字符串成员的问题。据我所知,应该有<=> for std::string的支持,但实际上似乎并非如此。
我有一个最小的复制例子,以及我从clang(版本14)得到的警告,奇怪的是,在godbolt中这个例子没有产生警告/错误,甚至可以追溯到clang 12(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)
dxpyg8gm

dxpyg8gm1#

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

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

当你写:

struct MyStruct
{
    float a{};
    int b{};
    std::string c{};
    MyEnum d{};

    auto operator<=>(const MyStruct&) const = default;
};

默认的<=>意味着使用<=>进行成员间比较,返回第一个比较结果不相等的成员的值。当使用auto时,这要求编译器为您推导比较类别-但与auto的典型用法不同(这里所有的返回类型必须相同,auto推导出一个类型),这里我们取所有比较类别中的最小值。如果有一个成员的<=>返回weak_ordering,而另一个成员的<=>返回strong_ordering,则默认的<=>将返回weak_ordering(而不是不编译)。基本上-它做了你可能想让它做的事情,以你期望的方式。
但是--有时默认<=>是不够的,如果你的一个成员实际上还没有提供<=>**(就像在这个例子中).在这种情况下,手动写出所有的比较是很乏味的.因此,有另一种方法可以默认<=>(从P1186):您可以显式提供比较类别:

struct MyStruct
{
    float a{};
    int b{};
    std::string c{};
    MyEnum d{};

    std::strong_ordering operator<=>(const MyStruct&) const = default;
};

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

  • 如果成员有<=>,就使用它,并且它必须满足比较类别的要求(在这种情况下,它可能会失败,因为floatpartial_ordering进行比较,而partial_ordering不满足strong_ordering的要求-所以它必须返回partial_ordering)。
  • 如果成员 * 不 * 提供<=>,则我们从==<合成三路比较。

因此,在本例中:

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::string确实提供了<=>,您的实现将自动地获得它,并且更加高效(因为上面的实现对于字符串来说真的不是一个很好的实现,您可能想要做c.compare(rhs.c) <=> 0),而不必自己做所有的工作。

相关问题