Спасибо за решение в 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 в этом случае?





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());
Дэйв, причина в том, что функторы могут быть встроены, поскольку тип функторов сообщает компилятору, какая функция вызывается во время компиляции. используя указатели на функции, компилятор знает это только во время выполнения, и он не может быть встроен.
@ onebyone.livejournal.com: Неправда. Указатель на функцию НЕ может быть встроен. Компилятору не разрешается производить такую оптимизацию. (Хотя указатель - это функтор).
@Konrad Rudolph: Ваш метод не работает так же, как оригинал: окончательный результат не должен сравнивать y, а должен просто возвращать false.
некоторые говорят, что указатель на нормальную функцию не является функтором, и говорят, что функторами являются только те типы классов, которые имеют перегруженную op (). но указатели на функции определенно являются объектами функций.
по одному. см.: bool (*) (T const &, T const &) может быть тот тип, который видит компилятор. Существует бесконечное количество функций этого типа, которые делают разные вещи. Но если у вас есть U, где U является классической перегрузкой op (), существует только одно разрешение перегрузки, которое сможет определить.
(при условии обычных свойств объекта функции. т.е. не является полиморфом)
Функцию ptr можно использовать, когда ожидается функтор, и в зависимости от вашего определения мы можем назвать ее функтором, если хотите, но она страдает от проблемы, заключающейся в том, что функция, которую она представляет, неизвестна во время компиляции, что делает ее сложно встроить. Перегрузка класса op () позволяет избежать этой проблемы.
Мне кажется безумным, что C не может встраивать указатель на функцию. Постоянный указатель, такой как & compare, никогда не может быть какой-либо другой функцией, кроме сравнения. Так почему бы не встроить его?
Зан. вы неправильно это понимаете. Дело в том, что в функции std :: sort функция не знает, что вызывает сравнение. он знает только, что вызывает какую-то функцию. если std :: sort может быть встроен, тогда действительно может быть встроена и сама функция. но для этого нужен хороший компилятор.
@litb, @Martin York - вам, ребята, нужно выяснить, разрешено ли компилятору встраивать и сравнивать или нет: litb говорит, что это разрешено, но слишком глупо, Мартин говорит, что это запрещено, потому что это указатель на функцию. tbh, я думал, что обычное «сравнение» - это указатель на функцию, и, очевидно, он может быть встроенным.
шаблон <void (* fptr) ()> void call () {fptr (); } // может быть встроен так же, как версия op () с перегрузкой типа класса.
onebyone, вот почему функторы не должны быть полиморфными. Вы должны получить книгу-шаблон (C++ Templates - The Complete Guide). Там ты объяснишь, в чем дело. Компилятору не запрещено встраивать вызовы по указателям на функции - ни в коем случае.
В 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;
});
Вам даже не нужно использовать функтор. Почему бы просто не использовать обычную функцию, в ней меньше строк кода?