После сильной боли и страданий я обнаружил очень странное поведение, когда std::distance
никогда не возвращается, если задан диапазон boost::filter_iterator
по сравнению с std::deque
. По-видимому, проблема уникальна для GCC (6.1+) с оптимизацией -O3
. Вот пример, демонстрирующий поведение с нарушением:
#include <string>
#include <deque>
#include <iterator>
#include <iostream>
#include <boost/iterator/filter_iterator.hpp>
struct Foo
{
std::string bar, s = "";
char a = '\0';
};
int main()
{
const std::deque<Foo> foos(14, {""});
const std::string test {};
const auto p = [test] (const auto& foo) { return foo.bar == test; };
using boost::make_filter_iterator;
const auto begin = make_filter_iterator(p, std::cbegin(foos), std::cend(foos));
const auto end = make_filter_iterator(p, std::cend(foos), std::cend(foos));
std::cout << std::distance(begin, end) << std::endl;
}
Некоторые наблюдения:
- GCC с оптимизацией
-O2
или меньше возвращается, как ожидалось. - Clang (3.8) возвращает правильный ответ с любым уровнем оптимизации.
- Изменение
std::deque
доstd::vector
илиstd::list
приводит к ожидаемому поведению. -
14
является критическим; что-то меньшее, и проблема исчезает. - Важна
sizeof(Foo)
; удалениеs
илиa
заставляет проблему уйти. - Захват
test
по ссылке или просто сравнение с константным выражением (например,foo.bar == " "
) приводит к нормальному поведению. - Предупреждений компилятора нет (с
-Wall -Wextra -pedantic
). - Valgrind не сообщает об ошибках.
- Используйте
fsanitize=undefined
, и проблема исчезнет.
Что происходит?