【问题标题】:C++ program behaviour different between optimizations优化之间的 C++ 程序行为不同
【发布时间】:2021-12-13 21:15:06
【问题描述】:

我的问题是关于这个小代码 sn-p:

typedef std::map<std::string, std::string> my_map_t;

std::string_view get_value_worse(const my_map_t& input_map, std::string_view value)
{
    auto retrieved = input_map.find(value.data());
    return retrieved != input_map.cend() ? retrieved->second : "";
}

std::string_view get_value_better(const my_map_t& input_map, std::string_view value)
{
    auto retrieved = input_map.find(value.data());
    if (retrieved != input_map.cend())
    {
        return retrieved->second;
    }
    return "";
}

int main()
{
    my_map_t my_map = {
        {"key_0", "value_0"},
        {"key_1", "value_1"},
    };

    std::cout << (get_value_worse(my_map, "key_0") == get_value_better(my_map, "key_0")) << std::endl;
}

在没有优化的最新 gcc 下,此打印 0 为 false,而在 -O3 下,此打印 1 为 true。

我相信未优化的行为是因为第二个和第三个比较运算符参数是表达式,而不是语句 - 因此 retrieved != arguments.cend() ? retrieved-&gt;second : "" 中的 retrieved-&gt;second 被评估为堆栈上的字符串构造,并将 string_view 返回到这很糟糕。

我还可以看到,使用 -O3 编译器将能够内联所有这些、删除分支并完成它......但我希望 -O3 能够完全正确 “好像”我是用 -O0 编译的。

谁能解释为什么编译器会忽略我认为在 -O0 版本中发生的复制构造?

【问题讨论】:

  • 对不起,我在代码中重命名了一些东西,可能会扰乱人们的回答流程
  • 两个版本都是未定义的行为。返回的 string_view 不拥有字符串数据。它归基础std::string 对象所有。在这两种情况下,当任一函数返回时,它都会超出范围并被销毁,从而为您留下一个已删除对象的 string_view。所有进一步使用它都是未定义的行为,并导致spontaneous demons flying out of everyone's nose
  • @SamVarshavchik 我认为get_value_better 不是未定义的,因为它为std::string 返回了string_view,而std::string 仍然存在于my_map 中?
  • @SamVarshavchik std::map::find 返回的迭代器指向std::map 中存在的实际对。 get_value_better 没有超出范围,完全没问题。

标签: c++ gcc


【解决方案1】:

在条件表达式中,构造了一个临时的std::string 对象。临时对象通常在 stack 上构建,尽管这是一个不重要的实现细节。重要的是临时对象在return语句结束时被销毁,所以返回的std::string_view是悬空的。尝试访问它指向的数据(使用 == 运算符或其他方式)会导致未定义的行为。

当程序包含未定义的行为时,编译器可以随心所欲地处理它。特别是,允许​​编译器通过假设暗示未定义行为的条件始终为假来进行优化。如果事实证明这个假设是错误,那么编译器就会摆脱困境(因为这意味着正在发生未定义的行为)。您的编译器究竟做出了什么样的假设尚不清楚。这也无关紧要,因为你不能依赖你现在看到的行为。你应该重写你的程序来删除未定义的行为。

【讨论】:

  • 糟糕,我已将堆编辑为堆栈。我确实是这个意思——只是把词混在一起了。感谢您的回答!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-24
  • 1970-01-01
  • 2019-10-27
  • 2012-10-01
  • 1970-01-01
  • 2016-03-20
  • 1970-01-01
相关资源
最近更新 更多