【问题标题】:boost::any_range<gsl::string_span<>> crash in Release modeboost::any_range<gsl::string_span<>> 在发布模式下崩溃
【发布时间】:2016-02-05 17:40:35
【问题描述】:

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

#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_rangegsl::string_span后面的函数f作为参数接收的字符串序列的实际表示(注意,提交将string_view更改为@987654326 @ 已在几个小时前向 GSL 制作)。

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

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

编辑

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

【问题讨论】:

  • 祝你调试成功。您是否尝试调试它?您可以在发布模式下进行调试。
  • @WarrenP 当然。但它在 Debug/RelWithDebInfo 模式下工作得非常好,甚至在发布模式下的 lambda 中有一些调试输出,这让调试变得非常困难:)

标签: c++ c++11 boost cpp-core-guidelines string-span


【解决方案1】:

事实证明,any_rangedereference 方法将返回对T 的引用,除非将Reference 类型指定为const T,从而创建对临时对象的悬空引用。这是由于使用了在any_iterator_interface.hpp 中定义的any_incrementable_iterator_interface::mutable_reference_type_generator

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

【讨论】:

  • 这个问题终于在 1.74 中修复了
【解决方案2】:

这是boost::range 中的一个错误,仅在 2020 年 2 月合并了一个修复程序,但未将其纳入1.73。该修复程序自 1.74 起提供

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

【讨论】:

    【解决方案3】:

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

    const typename C::value_type&amp; v

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

    template <class Cont> cstring_span(Cont& c)

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

    【讨论】:

    • 谢谢尼尔。但是,在这种情况下,Cont 被推断为std::string const,并且没有创建临时文件(我只是单步执行代码并查看调用堆栈)。此外,我会假设这样一个悬空引用也会使 Debug 版本崩溃。我会更深入地研究它并尝试找出问题所在。
    猜你喜欢
    • 2018-08-01
    • 1970-01-01
    • 2021-07-17
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 2017-07-14
    • 1970-01-01
    • 2020-03-01
    相关资源
    最近更新 更多