Как использовать std :: sort с вектором структур и функцией сравнения?

Спасибо за решение в C, теперь я хотел бы добиться этого на C++, используя std :: sort и vector:

typedef struct
{
  double x;
  double y;
  double alfa;
} pkt;

vector< pkt > wektor; заполнен с помощью push_back (); функция сравнения:

int porownaj(const void *p_a, const void *p_b)
{
  pkt *pkt_a = (pkt *) p_a;
  pkt *pkt_b = (pkt *) p_b;

  if (pkt_a->alfa > pkt_b->alfa) return 1;
  if (pkt_a->alfa < pkt_b->alfa) return -1;

  if (pkt_a->x > pkt_b->x) return 1;
  if (pkt_a->x < pkt_b->x) return -1;

  return 0;
}

sort(wektor.begin(), wektor.end(), porownaj); // this makes loads of errors on compile time

Что исправить? Как правильно использовать std :: sort в этом случае?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
12
0
38 015
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

std::sort выполняет функцию сравнения, отличную от той, что используется в qsort. Ожидается, что вместо возврата –1, 0 или 1 эта функция вернет значение bool, указывающее, является ли первый элемент меньше второго.

У вас есть две возможности: реализовать operator < для ваших объектов; в этом случае вызов sort по умолчанию без третьего аргумента будет работать; или вы можете переписать указанную выше функцию, чтобы добиться того же.

Обратите внимание, что в аргументах необходимо использовать строгую типизацию.

Кроме того, здесь хорошо вообще не использовать функцию. Вместо этого используйте объект функции. Им выгодно встраивание.

struct pkt_less {
    bool operator ()(pkt const& a, pkt const& b) const {
        if (a.alfa < b.alfa) return true;
        if (a.alfa > b.alfa) return false;

        if (a.x < b.x) return true;
        if (a.x > b.x) return false;

        return false;
    }
};

// Usage:

sort(wektor.begin(), wektor.end(), pkt_less());

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

Dave Hillier 30.11.2008 19:17

Дэйв, причина в том, что функторы могут быть встроены, поскольку тип функторов сообщает компилятору, какая функция вызывается во время компиляции. используя указатели на функции, компилятор знает это только во время выполнения, и он не может быть встроен.

Johannes Schaub - litb 30.11.2008 19:37

@ onebyone.livejournal.com: Неправда. Указатель на функцию НЕ может быть встроен. Компилятору не разрешается производить такую ​​оптимизацию. (Хотя указатель - это функтор).

Martin York 30.11.2008 20:50

@Konrad Rudolph: Ваш метод не работает так же, как оригинал: окончательный результат не должен сравнивать y, а должен просто возвращать false.

Martin York 30.11.2008 20:52

некоторые говорят, что указатель на нормальную функцию не является функтором, и говорят, что функторами являются только те типы классов, которые имеют перегруженную op (). но указатели на функции определенно являются объектами функций.

Johannes Schaub - litb 30.11.2008 20:52

по одному. см.: bool (*) (T const &, T const &) может быть тот тип, который видит компилятор. Существует бесконечное количество функций этого типа, которые делают разные вещи. Но если у вас есть U, где U является классической перегрузкой op (), существует только одно разрешение перегрузки, которое сможет определить.

Johannes Schaub - litb 30.11.2008 20:54

(при условии обычных свойств объекта функции. т.е. не является полиморфом)

Johannes Schaub - litb 30.11.2008 20:57

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

jalf 30.11.2008 22:03

Мне кажется безумным, что C не может встраивать указатель на функцию. Постоянный указатель, такой как & compare, никогда не может быть какой-либо другой функцией, кроме сравнения. Так почему бы не встроить его?

Zan Lynx 30.11.2008 22:44

Зан. вы неправильно это понимаете. Дело в том, что в функции std :: sort функция не знает, что вызывает сравнение. он знает только, что вызывает какую-то функцию. если std :: sort может быть встроен, тогда действительно может быть встроена и сама функция. но для этого нужен хороший компилятор.

Johannes Schaub - litb 30.11.2008 23:05

@litb, @Martin York - вам, ребята, нужно выяснить, разрешено ли компилятору встраивать и сравнивать или нет: litb говорит, что это разрешено, но слишком глупо, Мартин говорит, что это запрещено, потому что это указатель на функцию. tbh, я думал, что обычное «сравнение» - это указатель на функцию, и, очевидно, он может быть встроенным.

Steve Jessop 01.12.2008 02:08

шаблон <void (* fptr) ()> void call () {fptr (); } // может быть встроен так же, как версия op () с перегрузкой типа класса.

Johannes Schaub - litb 01.12.2008 02:35

onebyone, вот почему функторы не должны быть полиморфными. Вы должны получить книгу-шаблон (C++ Templates - The Complete Guide). Там ты объяснишь, в чем дело. Компилятору не запрещено встраивать вызовы по указателям на функции - ни в коем случае.

Johannes Schaub - litb 01.12.2008 02:36

В C++ вы можете использовать функторы, такие как boost::bind, которые прекрасно справляются с этой задачей:

#include <vector>
#include <algorithm>

struct pkt {
    double x;
    double y;
    double alfa;
    pkt(double x, double y, double alfa)
        :x(x), y(y), alfa(alfa) { }
};

int main() {
    std::vector<pkt> p;
    p.push_back(pkt(10., 0., 20.));
    p.push_back(pkt(10,  0., 30.));
    p.push_back(pkt(5.,  0., 40.));

    std::sort(p.begin(), p.end(), 
              boost::bind(&pkt::alfa, _1) <  boost::bind(&pkt::alfa, _2) || 
              boost::bind(&pkt::alfa, _1) == boost::bind(&pkt::alfa, _2) && 
              boost::bind(&pkt::x,    _1) <  boost::bind(&pkt::x,    _2));
}

Если вам нужно сделать это много раз, вы также можете решить проблему, создав объект функции, который принимает указатели на элементы и выполняет сортировку. Вы можете повторно использовать его для любых объектов и членов. Во-первых, как вы его используете:

int main() {
    /* sorting a vector of pkt */
    std::vector<pkt> p;
    p.push_back(pkt(10., 0., 20.));
    p.push_back(pkt(5.,  0., 40.));

    std::sort(p.begin(), p.end(), make_cmp(&pkt::x, &pkt::y));
}

Вот код для make_cmp. Не стесняйтесь копировать (используя boost::preprocessor):

#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/facilities/empty.hpp>

// tweak this to increase the maximal field count
#define CMP_MAX 10

#define TYPEDEF_print(z, n, unused) typedef M##n T::* m##n##_type;
#define MEMBER_print(z, n, unused) m##n##_type m##n;
#define CTORPARAMS_print(z, n, unused) m##n##_type m##n
#define CTORINIT_print(z, n, unused) m##n(m##n)

#define CMPIF_print(z, n, unused)              \
    if ((t0.*m##n) < (t1.*m##n)) return true;  \
    if ((t0.*m##n) > (t1.*m##n)) return false; \

#define PARAM_print(z, n, unused) M##n T::* m##n

#define CMP_functor(z, n, unused)                                       \
    template <typename T                                                \
              BOOST_PP_ENUM_TRAILING_PARAMS(n, typename M)>             \
    struct cmp##n {                                                     \
        BOOST_PP_REPEAT(n, TYPEDEF_print, ~)                            \
        BOOST_PP_REPEAT(n, MEMBER_print, ~)                             \
        cmp##n(BOOST_PP_ENUM(n, CTORPARAMS_print, ~))                   \
            BOOST_PP_IF(n, :, BOOST_PP_EMPTY())                         \
            BOOST_PP_ENUM(n, CTORINIT_print, ~) { }                     \
                                                                        \
        bool operator()(T const& t0, T const& t1) const {               \
            BOOST_PP_REPEAT(n, CMPIF_print, ~)                          \
            return false;                                               \
        }                                                               \
    };                                                                  \
                                                                        \
    template<typename T                                                 \
             BOOST_PP_ENUM_TRAILING_PARAMS(n, typename M)>              \
    cmp##n<T BOOST_PP_ENUM_TRAILING_PARAMS(n, M)>                       \
        make_cmp(BOOST_PP_ENUM(n, PARAM_print, ~))                      \
    {                                                                   \
        return cmp##n<T BOOST_PP_ENUM_TRAILING_PARAMS(n, M)>(           \
            BOOST_PP_ENUM_PARAMS(n, m));                                \
    }

BOOST_PP_REPEAT(CMP_MAX, CMP_functor, ~)


#undef TYPEDEF_print
#undef MEMBER_print
#undef CTORPARAMS_print
#undef CTORINIT_print
#undef CMPIF_print
#undef PARAM_print
#undef CMP_functor

С C++ 0x и лямбдами решение Конрада выглядит так:

sort(wektor.begin(), wektor.end(), [](pkt const& a, pkt const& b)
{
    if (a.alfa < b.alfa) return true;
    if (a.alfa > b.alfa) return false;

    if (a.x < b.x) return true;
    if (a.x > b.x) return false;

    return false;
});

Другие вопросы по теме