Nano Hash - криптовалюты, майнинг, программирование

std::sort вызывает ошибку сегментации в операторе‹

Я пытаюсь отсортировать список (std::vector) трехмерных целочисленных векторов (IntVec). Каким-то образом std::sort вызывает ошибку сегментации в operator< из IntVec. Вот мой код:

#include <iostream>
#include <algorithm>
#include <vector>
#include <fstream>

struct IntVec
{
public:
    long x;
    long y;
    long z; // Using ints does not cause the Segmentation Fault ?!

    friend bool operator<(const IntVec &lhs, const IntVec &rhs)
    {
        return (lhs.z < rhs.z) ||  // Segmentation Fault happens here
                    ((lhs.z == rhs.z) && (lhs.y < rhs.y))
                        || ((lhs.y == rhs.y) && (lhs.x < rhs.x));
    }
};

int main(void)
{
    std::vector<IntVec> vec;

    const int N = 2178;
    std::ifstream s("res.txt");
    for (int i = 0; i < N; i++)
    {
        IntVec t;
        s >> t.x;
        s >> t.y;
        s >> t.z;
        vec.push_back(t);
    }

    // Using vec.begin() and vec.end() does not change anything
    std::sort(vec.data(), vec.data() + vec.size());
}

Я могу предоставить вам набор данных; однако сначала я хотел посмотреть, есть ли у меня большая концептуальная ошибка в моем коде или какая-то ошибка, которую я не вижу. Я обнаружил, что проблема специфична для этого набора данных. Если я пропущу одну запись, Segfault не произойдет. Я думаю, что это довольно странно, так как такая ошибка должна быть более очевидной и связана с управлением памятью. Также обратите внимание, что использование целых чисел для x, y и z не вызывает никаких проблем.

Вот версия кода Godbolt.

Вот связанный вопрос SO. Однако я думаю, что в моем коде нет того недостатка, который вызвал эту ошибку. Я думаю, что мое отношение порядка "строго ‹".


  • Вы пытались запустить его с ASAN и UBSAN? 26.05.2020
  • Вы действительно должны инициализировать свои IntVec переменные-члены по умолчанию. 26.05.2020
  • Вы тестируете нарушение строгого слабого требования к порядку, поэтому std::sort имеет неопределенное поведение. 26.05.2020
  • [Совет]: при сравнении нескольких значений meber используйте std::tie, чтобы сделать это за вас. Это позволяет вам иметь return std::tie(lhs.z, lhs.y, lhs.x) < std::tie(rhs.z, rhs.y, rhs.x);, который чище и проще для понимания. 26.05.2020
  • обнаружено, что проблема связана с этим набором данных. Если я пропущу одну запись, Segfault не произойдет. -- Я не уверен, какой компилятор вы используете, но среда выполнения отладки Visual C++ проверяет наличие ошибки строгого-слабого порядка и выдает assert(), если строго -weak-order нарушается, а не просто сбой. Возможно, есть такой переключатель компилятора, который разрешает эту проверку для вашего набора инструментов компилятора. 26.05.2020
  • @PaulMcKenzie Я использовал g++-9 26.05.2020

Ответы:


1

Логика вашего оператора нарушена (не удовлетворяет строгому требованию слабого упорядочения). Последнее предложение тоже нуждается в lhs.z == rhs.z. В противном случае lhs.z может быть > rhs.z, но вы все равно получите положительный результат, что приведет к непоследовательному порядку.

Алгоритмы стандартных библиотек возлагают на вас ответственность за то, чтобы сделать это правильно, и нарушение результирующих предположений может легко привести к хаосу (читай: неопределенному поведению), такому как ошибки сегментации.

Представьте себе комментарий внутри реализации, говорящий что-то вроде «на данный момент мы знаем, что a меньше, чем b, поэтому нам не нужно выполнять какую-либо проверку диапазона/границы для b». Когда a неожиданно больше, чем b, отсутствие проверки границ может привести к неправильному доступу к памяти. Однако результат может быть гораздо более тонким и вызывать странные ошибки в дальнейшем, поэтому важно все сделать правильно.

Возможно, вы захотите рассмотреть возможность использования более короткого и менее подверженного ошибкам метода реализации этого порядка:

return std::tie(lhs.z, lhs.y, lhs.x) < std::tie(rhs.z, rhs.y, rhs.x);

Использование operator< для кортежа (что дает вам std::tie) автоматически (и правильно! ) выполнит для вас лексикографическую разбивку.

На самом деле есть хороший пример этого на странице cppreference для std::tie, показывая, что это обычное дело.

26.05.2020
  • О, парень. Я смутно помню (повторно) перенос двух скобок, когда я был в полусне... 26.05.2020
  • На один шаг лучше: используйте член std::tuple‹long,long,long› вместо x,y,x. 26.05.2020
  • @stefaanv x, y и z - лучшие имена, чем get<0>(), get<1>(), get<2>(), если вы говорите о точках в трехмерном пространстве. 26.05.2020
  • @stefaanv Нет, это запутывание без какой-либо выгоды, ИМО 26.05.2020
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..