使用clang优化编译时得到意外结果

Getting unexpected result when compiling with clang optimization

本文关键字:意外 结果 clang 优化 编译 使用      更新时间:2023-10-16

我在我的代码中发现了一个bug,只有当我启用编译器优化- 01或更高时才会发生。我跟踪了这个bug,似乎我不能在优化启用时在boost转换范围上使用boost type_erase适配器。我写了这个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

当我使用- 01标志运行它时,我得到以下结果:

1 1 1
2 3 4
1 2 3

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

Apple LLVM version 8.0.0 (clang-800.0.38)

我不知道是我做错了什么,还是它是一个boost/clang错误。

编辑:

改成

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

,它现在工作。第三个模板参数是引用类型,将引用设置为const会延长转换后创建的临时对象的时间范围。

EDIT事实上,这比你看到的要复杂得多。还有另一个可用性问题,它确实解决了这个问题。参见OP的自我回答


你正陷入Boost Range v2(和Boost Proto等)的1号陷阱。

nums | transformed(addOne)是临时的。type_erased适配器存储一个引用到它。

将类型擦除适配器赋值给resultN变量后,临时适配器将被销毁。

你得到的是一个悬空引用:(

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

这是一个解决方法:

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

-fsanitize=address,undefined在使用命名的临时时确认UB已经消失。

使用

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

。第三个模板参数是引用类型,将引用设置为const会延长转换后创建的临时对象的时间范围。