我正在观察下面一段代码的相当奇怪的行为:
#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));
}
这里的想法是隔离由f
和any_range
后面的函数gsl::string_span
作为参数接收的字符串序列的实际表示(注意,提交更改几小时前已向{GSL}提出string_view
到string_span
。
我的原始代码没有const T
Reference
模板参数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构建版本,行为类似(在调试中工作正常,不会崩溃,但输出带有const T
-O2
的垃圾。所以它似乎不像编译器问题。
答案 0 :(得分:2)
事实证明,除非将any_range
类型指定为dereference
,否则T
的{{1}}方法将返回对Reference
的引用},从而创建一个临时的悬挂引用。这是因为使用了any_iterator_interface.hpp中定义的const T
。
因此,正确解决问题的方法是将any_incrementable_iterator_interface::mutable_reference_type_generator
指定为const T
类型,以防迭代器解除引用返回临时值。
答案 1 :(得分:1)
这是boost::range
中的错误,并且仅在2020年2月合并了一个修复程序,但未将其合并到1.73
中。该修复程序自1.74
答案 2 :(得分:0)
快速浏览后,我怀疑问题出在你的lambda上。如果我理解正确,你最终会使用以下参数声明的const引用std::string
:
const typename C::value_type& v
但是,您正在使用v
构建cstring_span
。这里有一个问题:cstring_span
只有一个构造函数,它从非const引用变为容器类型(如std::string
)。从概念上讲,构造函数如下所示:
template <class Cont>
cstring_span(Cont& c)
所以我猜测当你从lambda返回时,会从v
创建一个临时的,然后传递给cstring_span
构造函数,以便提供一个非const引用参数。当然,一旦临时清理完毕,你的cstring_span
就会悬空。