Репро
Я могу воспроизвести с VS 2017 15.8.1, а также с последней версией 15.9.0 Preview 1.0 с / std: c ++ 17 или / std: c ++ последней.
Проблема возникает, только если _ITERATOR_DEBUG_LEVEL
не равно 0
, как правило, в конфигурациях отладки.
Причина
Рассматривая код STL, поставляемый с MSVC, мы видим, что _Array_const_iterator
имеет две разные реализации в зависимости от _ITERATOR_DEBUG_LEVEL
. Если _ITERATOR_DEBUG_LEVEL
не равно 0
, итератор сохраняет базовый указатель на массив и индексную переменную _Idx
типа std::size_t
. В противном случае хранится только указатель.
Часть проблемы вызвана _Array_const_iterator::operator+=()
, который косвенно вызывается std::prev()
со значением аргумента -1
:
_CONSTEXPR17 _Array_const_iterator& operator+=(const ptrdiff_t _Off)
{ // increment by integer
_Verify_offset(_Off);
_Idx += _Off; //<-- error C4308
return (*this);
}
Ошибка C4308 вызвана тем, что _Idx
не подписано, тогда как _Off
подписано, а фактическое (буквальное) значение _Off
отрицательно.
Еще более простой тестовый пример:
constexpr unsigned Test(unsigned x, int d) { x += d; return x; }
constexpr auto test1 = Test( 5, -1 ); //<-- error C4308
constexpr auto test2 = Test( 5, 1 ); //<-- OK
Присвоение test1
приводит к ошибке C4308 и предупреждению C4307. Я не уверен насчет C4146, возможно, это просто ошибка повторения.
MSVC выглядит более строгим, чем GCC, когда типы со знаком и без знака смешиваются в контексте constexpr
.
Возможно, MSFT предложит изменить тип переменной-члена _Idx
на ptrdiff_t
. Не стесняйтесь подавать ошибку: -).
Обход
Определите _ITERATOR_DEBUG_LEVEL = 0
или замените std::prev()
вызов:
constexpr iterator_type getIteratorBefore(iterator_type it) {
return --it;
}