Я наблюдаю довольно странное поведение следующего фрагмента кода:
#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
). Так что это не похоже на проблему с компилятором.
Как оказалось, any_range
«s dereference
метод вернет ссылку на T
если только Reference
тип указан как const T
, создавая тем самым висячую ссылку на временную. Это происходит из-за использования any_incrementable_iterator_interface::mutable_reference_type_generator
определяется в any_iterator_interface.hpp.
Поэтому правильное решение проблемы действительно заключается в const T
как Reference
введите, если разыменование итератора возвращает временное значение.
После быстрого взгляда, я подозреваю, что проблема заключается в вашей лямбде. Если я правильно понял, вы принимаете 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
осталось болтаться