【问题标题】:Getting unexpected result when compiling with clang optimization使用 clang 优化编译时得到意外结果
【发布时间】:2017-03-21 15:15:33
【问题描述】:

我在我的代码中发现了一个错误,该错误仅在我启用编译器优化 -O1 或更高版本时才会发生。我跟踪了这​​个错误,当启用优化时,我似乎无法在 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;
}

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

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 错误。

编辑:

改成

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

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

【问题讨论】:

  • result1 的计算中删除 type_erased 即使经过优化也会得到正确的结果。
  • 是的,我知道,这相当于 result2 是正确的。但我需要类型擦除的结果类型。我使用了 result2 和 result3 来证明两个适配器如果不一起使用也可以工作。
  • 我刚刚评论了我的观察。我对范围内容的了解为零:(。使用boost::any_iterator 怎么样?
  • 这就是我想要做的,这就是我使用 type_erased 适配器的方式。您不能在一个迭代器上进行迭代,您需要其中的两个,一个开始和结束。范围是一对迭代器(开始和结束)。类型擦除适配器 (type_erased) 应该返回一个 boost::any_range,它是一对 boost::any_iterator。

标签: c++ c++11 boost clang


【解决方案1】:

编辑事实上,这不仅仅是表面上看到的。还有另一个可用性问题,它确实解决了这个问题。见OP's self-answer


使用 Boost Range v2(和 Boost Proto 等),您陷入了 1 号陷阱。

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 已消失。

【讨论】:

  • 自动 tmp = nums |转换(添加一个);自动结果1 = tmp | type_erased();这仍然输出 1 1 1。
  • 那是什么编译器、平台和 boost 版本? (是否有 SSCCE 来检查...细微的差异)
  • 将 type_erased 引用类型设置为 const 解决了这个问题(请参阅我对问题的编辑)。我在 Mac OS Sierra 上使用 Apple LLVM 版本 8.0.0 (clang-800.0.38)。提升版本 1.62。
  • 哇。您可以将此添加为答案。不知何故,我想我现在有一半记得@_jv 早些时候发现了这个,我完全忘记了它。也不知道为什么了。您是否向 boost 开发者提出了问题?
  • @sehe clang 3.8 C++14 和 C++1z,Boost 1.62.0 失败,this test
【解决方案2】:

使用

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

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

【讨论】:

  • 我刚刚证实,事实上,这也会使我系统上的 ubsan/asan 和 valgrind 静音(实际上使用 temp 也消除了症状)。虽然没有尝试过clang。
猜你喜欢
  • 2019-11-29
  • 1970-01-01
  • 2023-02-26
  • 1970-01-01
  • 1970-01-01
  • 2014-07-28
  • 2022-11-01
  • 2021-02-14
  • 2021-05-08
相关资源
最近更新 更多