повышение :: any_range & Lt; GSL :: string_span & л; & GT; & GT; сбой в режиме выпуска

Я наблюдаю довольно странное поведение следующего фрагмента кода:

#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 (обратите внимание, изменение коммита string_view в string_span было сделано пару часов назад в GSL).

Мой оригинальный код не имел const T как Reference параметр шаблона для any_range (это было просто T) и он разбился во время исполнения. Тем не менее, это произошло только в режиме Release, который отлично работал в Debug или RelWithDebInfo (генерируется CMake). Я использовал VS2013 / 2015 x64. Кроме того, попытка отладки полной версии выпуска, добавление вывода отладки к лямбда-преобразованию устранила сбой (я предполагаю, что он предотвратил некоторую вставку). Мое окончательное рабочее решение — указать const T как Reference,

Однако мне все еще интересно Зачем произошла авария в первую очередь? Это ошибка компилятора VS? Ошибка в текущей реализации string_span? Или я просто неправильно использую boost::any_range?

редактировать

Только что собрал версию с clang 3.7.0 и поведение аналогичное (отлично работает при отладке и не вылетает, но выводит мусор без const T с -O2). Так что это не похоже на проблему с компилятором.

4

Решение

Как оказалось, any_range«s dereference метод вернет ссылку на T если только Reference тип указан как const T, создавая тем самым висячую ссылку на временную. Это происходит из-за использования any_incrementable_iterator_interface::mutable_reference_type_generator определяется в any_iterator_interface.hpp.

Поэтому правильное решение проблемы действительно заключается в const T как Reference введите, если разыменование итератора возвращает временное значение.

2

Другие решения

После быстрого взгляда, я подозреваю, что проблема заключается в вашей лямбде. Если я правильно понял, вы принимаете std::string по константной ссылке со следующим объявлением параметра:

const typename C::value_type& v

Тем не менее, вы затем используете v построить cstring_span, Вот в чем проблема: cstring_span имеет только конструктор, который идет от неконстантной ссылки к типу контейнера (например, std::string). Концептуально конструктор выглядит так:

template <class Cont>
cstring_span(Cont& c)

Итак, я предполагаю, что когда вы вернетесь из своей лямбды, создается временный v, а затем перешел к cstring_span конструктор для предоставления неконстантного ссылочного аргумента. Конечно, как только этот временный будет очищен, ваш cstring_span осталось болтаться

0