boost::any_range<gsl::string_span<>> crash in Release mode

眉间皱痕 提交于 2019-12-23 12:38:03

问题


I'm observing a rather weird behaviour of the following piece of code:

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

The idea here is to isolate the actual representation of a sequence of strings that is received as a parameter by the function f behind an any_range and gsl::string_span (note, the commit changing string_view to string_span has been made a couple of hours ago to GSL).

My original code did not have a const T as Reference template parameter to any_range (it was a simple T) and it crashed during execution. However, that happened only in Release mode an worked fine in Debug or RelWithDebInfo (generated by CMake). I used VS2013/2015 x64. Furthermore, trying to debug the full Release version, adding debug output to the conversion lambda eliminated the crash (my guess is it prevented some inlining). My final working solution is to specify const T as Reference.

However, I'm still wondering why did the crash happen in the first place? Is it the VS compiler bug? Bug in the current implementation of string_span? Or am I simply misusing the boost::any_range?

Edit

Just built the version with clang 3.7.0 and the behaviour is similar (works fine in debug and doesn't crash, but outputs garbage without const T with -O2). So it doesn't seem like a compiler problem.


回答1:


As it turns out, the any_range's dereference method will return a reference to T unless the Reference type is specified as const T, thus creating a dangling reference to a temporary. This happens due to use of any_incrementable_iterator_interface::mutable_reference_type_generator defined in any_iterator_interface.hpp.

Therefore, the correct solution to the problem is indeed to specify const T as the Reference type in case the iterator dereferencing returns a temporary.




回答2:


After a quick look, I suspect the problem lies in your lambda. If I understood correctly, you end up taking a std::string by const reference with the following parameter declaration:

const typename C::value_type& v

However, you are then using v to construct a cstring_span. Here's the rub: cstring_span only has a constructor that goes from a non-const reference to a container type (like std::string). Conceptually, the constructor looks like this:

template <class Cont> cstring_span(Cont& c)

So I am guessing that when you return from your lambda, a temporary is being created from v, and then passed to the cstring_span constructor in order to provide a non-const reference argument. Of course, once that temporary gets cleaned up, your cstring_span is left dangling.



来源:https://stackoverflow.com/questions/33535221/boostany-rangegslstring-span-crash-in-release-mode

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!