Сортировка проблем с использованием функции-члена в качестве компаратора
пытается скомпилировать следующий код. Я получаю эту ошибку компиляции, что я могу сделать?
ISO С++ запрещает принимать адрес неквалифицированный или заключенный в скобки нестатическая функция-член для формирования указатель на функцию-член.
class MyClass {
int * arr;
// other member variables
MyClass() { arr = new int[someSize]; }
doCompare( const int & i1, const int & i2 ) { // use some member variables }
doSort() { std::sort(arr,arr+someSize, &doCompare); }
};
Ответы
Ответ 1
doCompare
должен быть static
. Если doCompare
нужны данные из MyClass
, вы можете превратить MyClass
в функтор сравнения, изменив:
doCompare( const int & i1, const int & i2 ) { // use some member variables }
в
bool operator () ( const int & i1, const int & i2 ) { // use some member variables }
и вызов:
doSort() { std::sort(arr,arr+someSize, *this); }
Кроме того, не doSort
отсутствует возвращаемое значение?
Я думаю, что возможно использовать std::mem_fun
и некоторую привязку, чтобы превратить функцию-член в свободную функцию, но точный синтаксис меня уклоняется в данный момент.
EDIT: Doh, std::sort
принимает функтор по значению, что может быть проблемой. Чтобы обойти эту оболочку, функтор внутри класса:
class MyClass {
struct Less {
Less(const MyClass& c) : myClass(c) {}
bool operator () ( const int & i1, const int & i2 ) {// use 'myClass'}
MyClass& myClass;
};
doSort() { std::sort(arr,arr+someSize, Less(*this)); }
}
Ответ 2
Как говорит Андреас Бринк, doCompare должен быть статическим (+1). Если у вас есть состояние в вашей функции компаратора (с использованием других членов класса), вам лучше использовать функтор вместо функции (и это будет быстрее):
class MyClass{
// ...
struct doCompare
{
doCompare( const MyClass& info ) : m_info(info) { } // only if you really need the object state
const MyClass& m_info;
bool operator()( const int & i1, const int & i2 )
{
// comparison code using m_info
}
};
doSort()
{ std::sort( arr, arr+someSize, doCompare(*this) ); }
};
Использование функтора всегда лучше, просто дольше, чтобы печатать (это может быть неудобно, но хорошо...)
Я думаю, вы также можете использовать std:: bind с функцией-членом, но я не уверен, как и что будет непросто читать в любом случае.
UPDATE 2014: Сегодня у нас есть доступ к компиляторам С++ 11, поэтому вместо этого вы можете использовать лямбда, код будет короче, но будет иметь одинаковую семантику.
Ответ 3
Решение, предложенное Робом, теперь является верным С++ 11 (нет необходимости в Boost):
void doSort()
{
using namespace std::placeholders;
std::sort(arr, arr+someSize, std::bind(&MyClass::doCompare, this, _1, _2));
}
Действительно, как упоминал Клаим, лямбды - это вариант, более подробный (вы должны "повторить", что аргументы являются целыми числами):
void doSort()
{
std::sort(arr, arr+someSize, [this](int l, int r) {return doCompare(l, r); });
}
С++ 14 поддерживает auto
здесь:
void doSort()
{
std::sort(arr, arr+someSize, [this](auto l, auto r) {return doCompare(l, r); });
}
но, тем не менее, вы объявили, что аргументы передаются копией.
Тогда возникает вопрос "какой из них наиболее эффективен". Этот вопрос был рассмотрен трэвисом Гокелем: Лямбда против Бинда. Его тестовая программа выдает на моем компьютере (OS X i7)
Clang 3.5 GCC 4.9
lambda 1001 7000
bind 3716166405 2530142000
bound lambda 2438421993 1700834000
boost bind 2925777511 2529615000
boost bound lambda 2420710412 1683458000
где lambda
- это лямбда, используемая напрямую, а lambda bound
- это лямбда, хранящаяся в std::function
.
Таким образом, кажется, что лямбды - лучший вариант, что не является сюрпризом, так как компилятору предоставляется информация более высокого уровня, из которой он может получить прибыль.
Ответ 4
Вы можете использовать boost::bind
:
void doSort() {
std::sort(arr,arr+someSize, boost::bind(&MyClass::doCompare, this, _1, _2));
}
Ответ 5
Есть способ сделать то, что вы хотите, но вам нужно использовать небольшой адаптер. Поскольку STL не пишет это для вас, он может написать сам:
template <class Base, class T>
struct adaptor_t
{
typedef bool (Base::*method_t)(const T& t1, const T& t2));
adaptor_t(Base* b, method_t m)
: base(b), method(m)
{}
adaptor_t(const adaptor_t& copy) : base(copy.base), method(copy.method) {}
bool operator()(const T& t1, const T& t2) const {
return (base->*method)(t1, t2);
}
Base *base;
method_t method;
}
template <class Base, class T>
adaptor_t<Base,T> adapt_method(Base* b, typename adaptor_t<Base,T>::method_t m)
{ return adaptor_t<Base,T>(b,m); }
Затем вы можете использовать его:
doSort() { std::sort(arr,arr+someSize, adapt_method(this, &doCompare)); }
Ответ 6
Очень простой способ эффективного использования функции-члена - использовать оператор <. То есть, если у вас есть функция, называемая compare, вы можете вызвать ее из оператора <. Вот рабочий пример:
class Qaz
{
public:
Qaz(int aX): x(aX) { }
bool operator<(const Qaz& aOther) const
{
return compare(*this,aOther);
}
static bool compare(const Qaz& aP,const Qaz& aQ)
{
return aP.x < aQ.x;
}
int x;
};
Тогда вам даже не нужно указывать имя функции для std:: sort:
std::vector<Qaz> q;
q.emplace_back(8);
q.emplace_back(1);
q.emplace_back(4);
q.emplace_back(7);
q.emplace_back(6);
q.emplace_back(0);
q.emplace_back(3);
std::sort(q.begin(),q.end());
Ответ 7
Обновление ответа Грэма Ашера, так как вам не нужно сравнивать, но можно использовать оператора меньше.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Qaz {
public:
Qaz(int aX): x(aX) { }
bool operator<(const Qaz& aOther) const {
return x < aOther.x;
}
int x;
};
int main() {
std::vector<Qaz> q;
q.emplace_back(8);
q.emplace_back(1);
q.emplace_back(4);
q.emplace_back(7);
q.emplace_back(6);
q.emplace_back(0);
q.emplace_back(3);
std::sort(q.begin(),q.end());
for (auto& num : q)
std::cout << num.x << "\n";
char c;
std::cin >> c;
return 0;
}
Ответ 8
Третий аргумент в вызове std::sort()
не совместим с указателем на функцию, который необходим для std::sort()
. См. мой ответ на другой вопрос для подробного объяснения того, почему сигнатура функции-члена отличается от сигнатуры обычной функции.