我在代码中发现了一个错误,只有在我启用编译器优化-O1或更高版本时才会发生错误。我追踪了这个错误,似乎在启用优化时,我无法在升压转换范围内使用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)
我不知道我做错了什么,或者它是否是一个助推器/铿锵声。
修改
将其更改为
type_erased<int, boost::forward_traversal_tag, const int>()
现在可以使用了。第三个模板参数是引用类型,将引用设置为const可以延长转换后创建的临时值的时间跨度。
答案 0 :(得分:9)
编辑事实上,除此之外还有更多内容。还有另一个可用性问题,它解决了这个问题。见OP's self-answer
你在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已消失。
答案 1 :(得分:2)
使用
type_erased<int, boost::forward_traversal_tag, const int>()
的工作原理。第三个模板参数是引用类型,将引用设置为const可以延长转换后创建的临时值的时间跨度。