Вкратце, мой вопрос несколько языковой: является ли следующая проблема ошибкой в MSVC ++ или "приемлемым" поведением?
После обнаружения неприятного нарушения доступа в чьем-то коде я отследил источник проблемы до отладочной реализации std::priority_queue<>
в MSVS. (Я подтвердил, что проблема существует как в 2008, так и в 2010 году.) Ниже приведен пример кода, который иллюстрирует проблему:
#include <queue>
#include <iostream>
struct Less_than
{
bool operator() (const int *x, const int *y) const
{
std::cout << "Less_than(" << *x << ", " << *y << ")" << std::endl;
return *x < *y;
}
};
int main()
{
std::priority_queue<int *, std::vector<int *>, Less_than> q;
int x = 0;
int y = 1;
q.push(&x);
q.push(&y);
std::cout << "Popping " << *q.top() << "..." << std::endl;
q.pop();
}
Вывод должен быть (и есть в конфигурации сборки выпуска):
Less_than(0, 1)
Popping 1...
Тем не менее, в конфигурации отладки, вывод:
Less_than(0, 1)
Less_than(1, 0)
Popping 1...
Less_than(1, 0)
Ключевым отличием является окончательное дополнительное сравнение, которое возникает как побочный эффект pop (). В сборке Debug, когда pop () извлекает верхний элемент из очереди, реализация pop () сначала сравнивает верхний элемент со всеми другими элементами в очереди, чтобы убедиться, что это действительно максимальный элемент.
Это, очевидно, было бы проблемой (а было проблемой в рассматриваемом коде), если бы, как в приведенном выше примере, вы использовали priority_queue указателей на объекты с вашим собственным сравнением ... но также удалял объекты со следующим:
delete pq.top();
pq.pop();
Здесь мы освобождаем объект, на который указывает pq.top (), но затем, когда мы пытаемся выполнить pop () указатель из очереди, реализация отладки pop () сначала пытается сравнить недействительный объект, на который указывает к top () ко всему остальному в очереди!
Конечно, простое решение состоит в том, чтобы просто заменить вышеупомянутое на:
T *p = pq.top();
pq.pop();
delete p;
Но мой вопрос: технически разве оригинальный код не должен работать? Точнее говоря, можем ли мы зависеть от pop () , не сравнивающего текущий верхний элемент с чем-либо в процессе его удаления? Я читаю стандарт как довольно точный здесь; соответствующие разделы C ++ 03: 25.2.3.2.2 (pop()
) и 25.3.6.2 (pop_heap()
).
Заранее спасибо!