4

我观察到以下代码的一个相当奇怪的行为:

#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));
}

这里的想法是隔离一个字符串序列的实际表示,该字符串序列作为参数由andf后面的函数接收(注意,提交更改为几个小时前已在 GSL 中进行)。any_rangegsl::string_spanstring_viewstring_span

我的原始代码没有const TasReference模板参数any_range(它很简单T),并且在执行期间崩溃了。但是,这仅在发布模式下发生,在 Debug 或 RelWithDebInfo(由 CMake 生成)中运行良好。我使用了 VS2013/2015 x64。此外,尝试调试完整的 Release 版本,将调试输出添加到转换 lambda 消除了崩溃(我的猜测是它阻止了一些内联)。我最终的工作解决方案是指定const TReference.

但是,我仍然想知道为什么崩溃首先发生?它是VS编译器错误吗?当前实现中的错误string_span?还是我只是在滥用boost::any_range

编辑

刚刚使用 clang 3.7.0 构建了版本并且行为相似(在调试中工作正常并且不会崩溃,但在没有const Twith 的情况下输出垃圾-O2)。所以它似乎不是编译器问题。

4

3 回答 3

3

事实证明,除非类型指定为,否则any_range'dereference方法将返回对的引用,从而创建对临时对象的悬空引用。这是由于使用了any_iterator_interface.hpp中定义的。TReferenceconst Tany_incrementable_iterator_interface::mutable_reference_type_generator

因此,该问题的正确解决方案确实是指定const TReference类型,以防迭代器取消引用返回一个临时值。

于 2015-11-05T13:45:42.830 回答
1

这是一个错误,boost::range仅在 2020 年 2 月才合并修复,但未将其纳入1.73. 该修复程序自1.74

https://github.com/boostorg/range/pull/94

于 2020-10-30T03:53:18.110 回答
0

快速浏览后,我怀疑问题出在您的 lambda 上。如果我理解正确,您最终std::string会使用以下参数声明通过 const 引用:

const typename C::value_type& v

但是,您随后将v用于构造一个cstring_span. 这就是问题所在:cstring_span只有一个从非常量引用到容器类型(如std::string)的构造函数。从概念上讲,构造函数如下所示:

template <class Cont> cstring_span(Cont& c)

所以我猜当你从你的 lambda 返回时,会创建一个临时对象v,然后传递给cstring_span构造函数以提供一个非常量引用参数。当然,一旦那个临时的被清理干净,你cstring_span就悬而未决。

于 2015-11-05T02:19:47.573 回答