升压:: any_range <GSL :: string_span <>&GT;发布模式

时间:2015-11-05 01:36:40

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

我正在观察下面一段代码的相当奇怪的行为:

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

这里的想法是隔离由fany_range后面的函数gsl::string_span作为参数接收的字符串序列的实际表示(注意,提交更改几小时前已向{GSL}提出string_viewstring_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的垃圾。所以它似乎不像编译器问题。

3 个答案:

答案 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

起可用

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

答案 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就会悬空。