boost::range::detail::any_iterator не очень хорошо работает с boost::zip_iterator

Рассмотрим следующий код:

#include <boost/iterator/zip_iterator.hpp>
#include <boost/range/detail/any_iterator.hpp>
#include <boost/tuple/tuple.hpp>
#include <iostream>
#include <vector>

typedef boost::range_detail::any_iterator<
  boost::tuple<int &, char &>,
  boost::random_access_traversal_tag,
  boost::tuple<int &, char &> &,
  std::ptrdiff_t
> IntCharIterator;

int main()
{
  std::vector<int> v1 = {1, 2, 3, 4, 5};
  std::vector<char> v2 = {'a', 'b', 'c', 'd', 'e'};

  auto it = IntCharIterator(boost::make_zip_iterator(
    boost::make_tuple(v1.begin(), v2.begin()))
  );
  auto end_ = IntCharIterator(boost::make_zip_iterator(
    boost::make_tuple(v1.end(), v2.end()))
  );

  for (; it != end_; ++it)
    std::cerr << it->get<0>() << " " << it->get<1>() << "\n";

  return 0;
}

Он работает должным образом (то есть печатает "1 a\n2 b..."), когда компилируется без оптимизации, но либо segfaults, либо производит мусор при компиляции с -O2 (как clang-3.6.0, так и gcc-4.9.2, буст 1.56.0) и я понятия не имею, что не так.

Кроме того, когда обертка IntCharIterator удаляется, код работает должным образом с любым уровнем оптимизации.

Кто-нибудь знает, что здесь происходит?

1 ответ

Решение

Это ошибка в Boost.Range: # 10493 Начиная с 1.56, any_range с нереференсными ссылками может вызывать UB (предупреждение: в настоящее время средство отслеживания ошибок имеет недействительный сертификат SSL). Это была регрессия, введенная исправлением для ошибки # 5816 any_range требует копируемых элементов.

Как ни странно, обходной путь состоит в том, чтобы сделать ваш Reference параметр типа шаблона const:

typedef boost::range_detail::any_iterator<
  boost::tuple<int &, char &>,
  boost::random_access_traversal_tag,
  boost::tuple<int &, char &> const,    // 'const', no '&'
  std::ptrdiff_t
> IntCharIterator;

Если вы хотите, чтобы код работал с версиями до 1.56, вы можете использовать условный препроцессор:

typedef boost::range_detail::any_iterator<
  boost::tuple<int &, char &>,
  boost::random_access_traversal_tag,
#if BOOST_VERSION < 105600
  boost::tuple<int &, char &>,          // no '&'
#else
  boost::tuple<int &, char &> const,    // 'const', no '&'
#endif
  std::ptrdiff_t
> IntCharIterator;

Обратите внимание, что в любом случае Reference Параметр типа шаблона не должен иметь &; за zip_iterator краткий обзорreference_type такой же, как value_type, так как это кортеж ссылок:

typedef reference value_type;
Другие вопросы по тегам