gpt4 book ai didi

c++ - 使用 C++20 三向比较进行更静默的行为变化

转载 作者:行者123 更新时间:2023-12-04 04:27:33 25 4
gpt4 key购买 nike

令我惊讶的是,我遇到了另一个障碍,例如 C++20 behaviour breaking existing code with equality operator? .
考虑一个简单的不区分大小写的键类型,用于例如 std::setstd::map :

// Represents case insensitive keys
struct CiKey : std::string {
using std::string::string;
using std::string::operator=;

bool operator<(CiKey const& other) const {
return boost::ilexicographical_compare(*this, other);
}
};
简单测试:
using KeySet   = std::set<CiKey>;
using Mapping = std::pair<CiKey, int>; // Same with std::tuple
using Mappings = std::set<Mapping>;

int main()
{
KeySet keys { "one", "two", "ONE", "three" };
Mappings mappings {
{ "one", 1 }, { "two", 2 }, { "ONE", 1 }, { "three", 3 }
};

assert(keys.size() == 3);
assert(mappings.size() == 3);
}
  • 使用 C++17,两个断言都通过 (Compiler Explorer)。
  • 切换到 C++20,第二个断言失败 (Compiler Explorer)

    output.s: ./example.cpp:28: int main(): Assertion `mappings.size() ==3' failed.



  • 明显的解决方法
    一个明显的解决方法是有条件地提供 operator<=>在 C++20 模式下: Compile Explorer
    #if defined(__cpp_lib_three_way_comparison)
    std::weak_ordering operator<=>(CiKey const& other) const {
    if (boost::ilexicographical_compare(*this, other)) {
    return std::weak_ordering::less;
    } else if (boost::ilexicographical_compare(other, *this)) {
    return std::weak_ordering::less;
    }
    return std::weak_ordering::equivalent;
    }
    #endif
    问题
    令我惊讶的是,我遇到了另一种破坏性更改的情况——C++20 在没有诊断的情况下更改了代码的行为。
    在我的 reading of std::tuple::operator< 它应该有效:

    3-6) Compares lhs and rhs lexicographically by operator<, that is,compares the first elements, if they are equivalent, compares thesecond elements, if those are equivalent, compares the third elements,and so on. For non-empty tuples, (3) is equivalent to

    if (std::get<0>(lhs) < std::get<0>(rhs)) return true;
    if (std::get<0>(rhs) < std::get<0>(lhs)) return false;
    if (std::get<1>(lhs) < std::get<1>(rhs)) return true;
    if (std::get<1>(rhs) < std::get<1>(lhs)) return false;
    ...
    return std::get<N - 1>(lhs) < std::get<N - 1>(rhs);

    我知道从技术上讲,这些从 C++20 开始就不再适用,它被替换为:

    Compares lhs and rhs lexicographically by synthesized three-waycomparison (see below), that is, compares the first elements, if theyare equivalent, compares the second elements, if those are equivalent,compares the third elements, and so on


    和...一起

    The <, <=, >, >=, and != operators are synthesized from operator<=> and operator== respectively. (since C++20)


    事情是,
  • 我的类型没有定义 operator<=>也不是 operator== ,
  • this answer points out提供operator<另外会很好,应该在评估像 a < b 这样的简单表达式时使用。 .
  • C++20 中的行为更改是否正确/故意?
  • 应该有诊断吗?
  • 我们可以使用其他工具来发现像这样的无声破损吗?感觉就像扫描整个代码库以使用 tuple 中的用户定义类型/pair不能很好地扩展。
  • 除了tuple还有其他类型吗?/pair这可能会表现出类似的变化?
  • 最佳答案

    基本问题来自于您的类型不连贯并且标准库直到 C++20 才调用您的事实。也就是说,你的类型总是有点坏,但事情的定义足够狭隘,你可以侥幸逃脱。
    您的类型已损坏,因为它的比较运算符没有意义。它宣称它是完全可比较的,并定义了所有可用的比较运算符。发生这种情况是因为您公开继承了 std::string ,因此您的类型通过隐式转换为基类来继承这些运算符。但是这种比较表的行为是不正确的,因为您只替换了其中一个比较与其他比较不起作用。
    而且由于行为不一致,一旦 C++ 真正关心你的一致性,可能发生的事情就可以解决了。
    然而,更大的问题是与标准处理 operator<=> 的方式不一致。 .
    C++ 语言的设计目的是在使用综合运算符之前优先考虑显式定义的比较运算符。所以你的类型继承自 std::string将使用您的 operator<如果你直接比较它们。
    然而,C++ 库有时会尝试变得聪明。
    某些类型尝试转发给定类型提供的运算符,例如 optional<T> .它的设计行为与 T 相同。在它的可比性上,它在这方面取得了成功。
    但是,pairtuple试着聪明一点。在 C++17 中,这些类型实际上从未转发比较行为;相反,它基于现有的 operator< 综合比较行为和 operator==类型的定义。
    因此,他们的 C++20 版本延续了综合比较的优良传统也就不足为奇了。当然,由于该语言进入了该游戏,C++20 版本决定最好遵循他们的规则。
    除了......它不能完全跟随他们。无法检测 < 是否存在比较是综合的或用户提供的。因此,无法以其中一种类型实现语言行为。但是,您可以检测到存在三向比较行为。
    所以他们做了一个假设:如果你的类型是三向可比的,那么你的类型依赖于综合运算符(如果不是,它使用旧方法的改进形式)。这是正确的假设;毕竟,自从 <=>是一项新功能,旧类型可能无法获得。
    当然,除非旧类型继承自获得三向可比性的新类型。并且类型也无法检测到这一点。它要么是三向可比的,要么不是。
    现在幸运的是,pair 的综合三路比较运算符和 tuple如果您的类型不提供三向比较功能,则完全能够模仿 C++17 行为。因此,您可以通过删除 operator<=> 显式地取消继承 C++20 中的三向比较运算符来恢复旧行为。重载。
    或者,您可以使用私有(private)继承并简单地公开 using您想要的特定 API。

    Is the behavior change in c++20 correct/on purpose?


    这取决于您所说的“故意”。
    std::string 等类型公开继承在道德上一直有些可疑。与其说是因为切片/析构函数问题,不如说是因为它有点作弊。直接继承此类类型会导致 API 发生您未预料到且可能不适合您的类型的更改。 pair的新比较版和 tuple正在做他们的工作,并在 C++ 允许的范围内做到最好。只是你的类型继承了它不想要的东西。如果您从 std::string 私下继承并且只有 using -暴露了您想要的功能,您的类型可能会很好。

    Should there be a diagnostic?


    这无法在某些编译器内在之外进行诊断。

    Can we use other tools to spot silent breakage like this?


    搜索您从标准库类型公开继承的情况。

    关于c++ - 使用 C++20 三向比较进行更静默的行为变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66497269/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com