【问题标题】:Operator() crashing program on MSVC C++17 (2019)MSVC C++17 (2019) 上的 Operator() 崩溃程序
【发布时间】:2021-05-27 22:37:20
【问题描述】:

以下代码不适用于 MSVC++ 2019,但适用于 GCC 编译器。

#include <set>
#include <string>
#include <iostream>

struct MyData {

    MyData() {}
    MyData(std::string keyA, std::string keyB) :keyA(keyA), keyB(keyB) {}

    std::string keyA;
    std::string keyB;
};


struct Compare {

    bool operator() (const MyData& lhs, const MyData& rhs) const
    {
        if (lhs.keyA < rhs.keyA)
            return true;

        if (lhs.keyB < rhs.keyB)
            return true;

        else
            // All else conditions would be false
            return false;
    }
};

int main()
{
    std::set<MyData, Compare> s;

    s.insert(MyData("Clark", "Alice"));
    s.insert(MyData("Bob", "Alice"));
    s.insert(MyData("Alice", "Bob"));
    s.insert(MyData("Derek", "Clark"));

    for (auto& i : s)
    {
        std::cout << i.keyA << ", " << i.keyB << std::endl;
    }
}

这是 MSVC 上的错误:

在 GCC 上,它显示以下输出:

Alice, Bob
Bob, Alice
Clark, Alice
Derek, Clark

Process returned 0 (0x0)   execution time : 0.971 s
Press any key to continue.

这个错误是什么原因造成的,如何正确定义算子?

【问题讨论】:

  • 如果lhs.keyA == "Bob"rhs.keyA == "Alice" 会发生什么?
  • 请注意,您可以使用 std::tie 非常紧凑地执行字典比较
  • 注意:如果您还没有注意到,按重试调试应用程序是给程序员的最好建议。
  • Visual Studio 确实在为调试配置生成的可执行文件中添加了一些检查。在这种情况下,我猜问题是您的比较器不是反对称的。
  • 输出不是您想要的。您想查看导致失败的值。如果你知道这些,你就会很清楚在哪里放置断点以便在程序失败之前停止程序,然后逐步检查问题区域以查看导致失败的原因。

标签: c++ set comparator


【解决方案1】:

您的比较器不符合strict weak ordering的规则,也就是说可能出现以下情况:

a < b
b < c
a >= c

MSVC 显然已检测到这一点并引发了断言失败。

要解决此问题,您可以按如下方式更改比较器:

bool operator() (const MyData& lhs, const MyData& rhs) const
{
    if (lhs.keyA < rhs.keyA)
        return true;

    if (lhs.keyA == rhs.keyA && lhs.keyB < rhs.keyB)
        return true;

    return false;
}

或者,更简洁,使用std::tie

bool operator() (const MyData& lhs, const MyData& rhs) const
{
    return std::tie (lhs.keyA, lhs.keyB) < std::tie (rhs.keyA, rhs.keyB);
}

我也去掉了多余的else

【讨论】:

  • 如果此代码适用于 GCC,是否意味着我为 GCC 正确定义了这种“严格的弱排序”?
  • 不,它没有,只是gcc 没有检查。
  • 不提供保证严格弱排序导致未定义行为的比较器,关于未定义行为的真正酷的事情之一是实现者可以插入检查。 Microsoft 的调试库包含许多 C++ 标准未明确要求的检查。它几乎总是非常有帮助。如果您编译了 Release 版本,您可能会发现您的代码“有效”,因为所有这些额外检查都需要时间,并且 Release 版本使用不同的库,这些库更快,部分原因是它们不进行这些额外检查。
  • 试图理解这种严格的弱排序意味着什么,所以我可以像 this 和 3 个键 this 那样定义它吗?
  • 是的,这行得通,尽管最后你有一些多余的测试。
【解决方案2】:

Compare::operator() 不正确。不符合strict weak ordering的条件。

你可以改成:

bool operator() (const MyData& lhs, const MyData& rhs) const
{
    if (lhs.keyA != rhs.keyA)
        return (lhs.keyA < rhs.keyA)
        
    return (lhs.keyB < rhs.keyB);
}

可以使用std::tie进一步简化。

bool operator() (const MyData& lhs, const MyData& rhs) const
{
    return std::tie(lhs.keyA, lhs.keyB) < std::tie(rhs.keyA, rhs.keyB);
}

【讨论】:

    猜你喜欢
    • 2018-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-08
    • 1970-01-01
    相关资源
    最近更新 更多