我正在观察下面这段代码的一个相当奇怪的行为:
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/any_range.hpp>
#include <vector>
#include <string>
#include <iostream>
#include "gsl.h"
template <typename T>
using ImmutableValueRange = boost::any_range<T, boost::bidirectional_traversal_tag, /*const*/ T>;
template <typename T, typename C>
ImmutableValueRange<T> make_transforming_immutable_range(const C& container)
{
return container | boost::adaptors::transformed([](const typename C::value_type& v) -> T
{
//std::cout << "trans : " << T{ v }.data() << "\n";
return T{ v };
});
}
void f(ImmutableValueRange<gsl::cstring_span<>> r)
{
for (const auto& c : r) {
std::cout << c.data() << "\n";
}
}
int main()
{
std::vector<std::string> v({ "x", "y", "z" });
f(make_transforming_immutable_range<gsl::cstring_span<>>(v));
}
这里的想法是隔离一个字符串序列的实际表示形式,这些字符串由any_range和gsl :: string_span后面的函数f作为参数接收(注意,提交将string_view更改为string_span已经在几个小时前发布到GSL).
我的原始代码没有const T作为any_range的参考模板参数(它是一个简单的T)并且在执行期间崩溃了.但是,这只发生在Release模式下,在Debug或RelWithDebInfo(由CMake生成)中运行正常.我使用的是VS2013 / 2015 x64.此外,尝试调试完整的Release版本,将调试输出添加到转换lambda消除了崩溃(我的猜测是它阻止了一些内联).我最后的工作解决方案是将const T指定为Reference.
但是,我仍然想知道为什么崩溃发生在第一位?是VS编译器错误吗?在当前执行string_span的错误?或者我只是滥用boost :: any_range?
编辑
刚刚使用clang 3.7.0构建版本,行为类似(在调试中运行正常,不会崩溃,但输出带有-O2的const T的垃圾).所以它似乎不像编译器问题.
最佳答案 事实证明,any_range的dereference方法将返回对T的引用,除非将Reference类型指定为const T,从而创建对临时的悬空引用.这是因为使用了
any_iterator_interface.hpp中定义的any_incrementable_iterator_interface :: mutable_reference_type_generator.
因此,正确解决问题的方法是在迭代器解除引用返回临时值的情况下将const T指定为Reference类型.