使用 clang 优化编译时出现意外结果

2024-02-21

我在代码中发现了一个错误,该错误仅在启用编译器优化 -O1 或更高版本时才会发生。我跟踪了该错误,似乎在启用优化时我无法在升压转换范围上使用升压 type_erased 适配器。我编写了这个 C++ 程序来重现它:

#include <iostream>
#include <vector>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptor/type_erased.hpp>

using namespace boost::adaptors;
using namespace std;

int addOne(int b) {
  return b + 1;
}

int main(int, char**) {
  vector<int> nums{ 1, 2, 3 };

  auto result1 = nums | transformed(addOne) | type_erased<int, boost::forward_traversal_tag>();
  auto result2 = nums | transformed(addOne);
  auto result3 = nums | type_erased<int, boost::forward_traversal_tag>();

  for (auto n : result1)
    cout << n << " ";
  cout << endl;

  for (auto n : result2)
    cout << n << " ";
  cout << endl;

  for (auto n : result3)
    cout << n << " ";
  cout << endl;
}

当我在没有任何优化的情况下运行该程序时,我得到以下输出:

2 3 4
2 3 4
1 2 3

当我使用 -O1 标志运行它时,我得到以下信息:

1 1 1
2 3 4
1 2 3

我正在使用 clang++ 来编译它。我使用的 clang 版本是:

Apple LLVM 版本 8.0.0 (clang-800.0.38)

我不知道我是否做错了什么,或者是否是 boost/clang bug。

edit:

改为

type_erased<int, boost::forward_traversal_tag, const int>()

现在可以了。第三个模板参数是引用类型,将引用设置为 const 可以延长转换后创建的临时变量的时间跨度。


EDIT事实上,事情的真相比表面上看到的还要复杂。还有另一个可用性问题,它确实解决了这个问题。看OP的自我回答 https://stackoverflow.com/a/40492610/85371


您正陷入 Boost Range v2(以及 Boost Proto 等)的第一个陷阱。

nums | transformed(addOne)是暂时的。这type_erased适配器商店一个参考对此。

将类型擦除的适配器分配给resultN变量,临时变量被破坏。

你所拥有的是一个悬空的参考:(

这是一种非常不直观的效果,也是我在代码库中限制使用 Range V2 的第一个原因:我已经经常出现这种情况了。

这是一个解决方法:

auto tmp = nums | transformed(addOne);
auto result = tmp | type_erased<int, boost::forward_traversal_tag>();

-fsanitize=address,undefined确认使用指定临时文件时 UB 已消失。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 clang 优化编译时出现意外结果 的相关文章

随机推荐