Карта STL повреждена значениями mpz_class (GMP)

У меня есть std::map<mpz_class,int> (для тех, кто не знаком, mpz_class — это контейнер класса для очень большого целого числа, определяемый GMP, Gnu Multiprecision Library). Я использую собственный компаратор, который использует функцию cmp() GMP. На карте я вставил несколько std::pair<mpz_class,int> с правильными значениями (они разумны, когда я их печатаю).

Однако я заметил, что map::find работает неправильно, поэтому я напечатал то, что сравнивает компаратор. Оказывается, второй элемент (ключ) всегда представляет собой целочисленное значение с очень диким значением, например 128957236027369832796823768439267, что выходит за рамки целых чисел, с которыми я работаю.

Происходит ли какое-то повреждение памяти, о котором я не знаю? Возможно, mpz_class нельзя использовать таким образом? Как мне обойти эту проблему? У меня не было этой проблемы с другими контейнерами до сих пор.

#include <map>
#include <gmpxx.h>
#include <iostream>

struct Equaler {
    inline bool operator()(const mpz_class a, const mpz_class b) const {
        std::cout << " about to return " << a << "," << b << "," << cmp(a,b) << "\n";
        return cmp(a, b);
    }
};

int main() {
    mpz_class x("38268");
    std::map<mpz_class,int,Equaler> map;
    map.insert(std::pair<mpz_class,int>(x,42));
    map.find(x);
    return 0;
}

Выход:

about to return 38268,812462232382732367817613904064203084469901797507,-2

person bombax    schedule 08.11.2013    source источник
comment
Покажи свой код. Полный компилируемый пример, демонстрирующий проблему, а также ожидаемые и фактические результаты.   -  person Benjamin Lindley    schedule 09.11.2013
comment
Разве вы не можете просто использовать std::map<mpz_class, int>?   -  person Kerrek SB    schedule 09.11.2013
comment
Побочный комментарий: строка map.insert(std::pair<mpz_class,int>(x,42)); может и неправильная, но она глупая: посмотрите на map::value_type, и если вы не против скопировать, там есть make_pair.   -  person Marc Glisse    schedule 09.11.2013


Ответы (1)


Проблема в компараторе. std::map ожидает компаратор, который возвращает true, если первый операнд следует считать меньшим, чем второй, и false в противном случае. Но cmp работает по-другому. Он не возвращает логическое значение, он возвращает целое число в одном из трех возможных состояний:

  • отрицательный: левый ‹ правый
  • 0 : левый == правый
  • положительный : левый > правый

Однако и отрицательное, и положительное целое число в логическом контексте оцениваются как истина, поэтому результаты cmp не преобразуются правильно в то, что ожидает std::map. Измените это:

return cmp(a, b);

к этому:

return cmp(a, b) < 0;
person Benjamin Lindley    schedule 08.11.2013
comment
Вероятно, это была дополнительная проблема, но, боюсь, проблема осталась. Забавно, потому что когда я запускаю тестовую программу, она работает, как и ожидалось. - person bombax; 09.11.2013
comment
На самом деле, я слишком быстро заговорил. Теперь это действительно работает - хотя это странно, потому что второй элемент по-прежнему печатает 812462232382732367817613904064203084469901797507 и такие абсурдные значения. Возможно, внутренняя карта делает что-то забавное, например, сравнивает с недопустимыми местоположениями? Тем не менее, немного сбивая с толку, я хотел бы знать, почему цифры выглядят такими абсурдными. - person bombax; 09.11.2013
comment
@bombax: Какую реализацию вы используете? Здесь я получаю более понятные результаты: coliru.stacked-crooked.com/a/c1f152ef878f1f8f - person Benjamin Lindley; 09.11.2013
comment
Я использую gcc на ubuntu. - person bombax; 09.11.2013
comment
mpz_class имеют перегрузки для operator<, поэтому, по-видимому, вы можете отказаться от cmp и просто return a < b; Это означает, что, если вы используете печать только для отладки, вам вообще не нужен компаратор, ваши ключи уже сопоставимы. - person ShadowRanger; 23.12.2016