Ошибка std :: priority_queue / проблема в MSVC ++ 2008/2010 Настройка отладки - PullRequest
2 голосов
/ 10 июня 2011

Вкратце, мой вопрос несколько языковой: является ли следующая проблема ошибкой в ​​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()).

Заранее спасибо!

1 Ответ

2 голосов
/ 10 июня 2011

Я бы сказал, что ваш код T *p = pq.top(); неверен. Вы получаете const & для элемента в вашей очереди. Но вы удаляете объект, который вы получаете. Таким образом, вы сделали объект в своей очереди недействительным.
Так что это опасная вещь. Я бы избегал делать такие вещи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...