使用clang优化进行编译时获得意外结果

use*_*609 5 c++ boost clang c++11

我在代码中发现了一个错误,只有在我启用编译器优化-O1或更高版本时才会发生错误.我追踪了这个bug,似乎在启用优化时我无法在boost转换范围上使用boost 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;
}
Run Code Online (Sandbox Code Playgroud)

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

2 3 4
2 3 4
1 2 3
Run Code Online (Sandbox Code Playgroud)

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

1 1 1
2 3 4
1 2 3
Run Code Online (Sandbox Code Playgroud)

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

Apple LLVM 8.0.0版(clang-800.0.38)

我不知道我做错了什么,或者它是否是一个助推器/铿锵声.

编辑:

把它改成了

type_erased<int, boost::forward_traversal_tag, const int>()
Run Code Online (Sandbox Code Playgroud)

它现在有效.第三个模板参数是引用类型,将引用设置为const可以延长转换后创建的临时值的时间跨度.

seh*_*ehe 9

编辑事实上,除此之外还有更多的东西.还有另一个可用性问题,它解决了这个问题.见OP的自我回答


你在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>();
Run Code Online (Sandbox Code Playgroud)

-fsanitize=address,undefined 确认在使用指定的临时时UB已消失.


use*_*609 2

使用

type_erased<int, boost::forward_traversal_tag, const int>()
Run Code Online (Sandbox Code Playgroud)

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