Каким образом функции-члены могут сравниваться друг с другом? - PullRequest
1 голос
/ 12 июля 2011

Я хотел бы знать, могу ли я сравнить 2 функции-члена с оператором "<". Я могу сделать "==", но я не могу использовать его в случае ниже. Я попытался привести их к <strong>void *, но это тоже не сработает.

template <class Receiver, class Sender>
class CallBack2 : public ICallBack2 {

protected:

    Receiver* receiver;
    void(Receiver::*function)(Sender*);
    Sender* sender;

public:

    CallBack2(Receiver* _receiver, void(Receiver::*_function)(Sender*), Sender* _sender) : receiver(_receiver), function(_function), sender(_sender) {};
    virtual ~CallBack2() {};

    virtual void callBack() {
        (receiver->*function)(sender);
    }

    virtual bool operator<(const ICallBack2* _other) const {
        CallBack2<Receiver, Sender>* other = (CallBack2<Receiver, Sender>*)_other;
        if (receiver < other->receiver) {
            return true;
        } else if (receiver == other->receiver && function < other->function) {
            return true; // this line gives the error
        }
        return false;
    }
};

Есть идеи, пожалуйста?

Ответы [ 7 ]

3 голосов
/ 12 июля 2011

C ++ 03 § 5.9 , который охватывает семантику встроенных <,>, <= и> =, не упоминает указатели на члены и состояния:

Другие сравнения указателей не определены.

Согласно § 8.3.3 , 3

Тип "указатель на член" имеет видВ отличие от типа «указатель»,

В результате мы можем заключить, что результат реляционных операторов, примененных к указателям на элементы (будь то функции или поля), не определен.

Обратите внимание, что «неопределенное поведение» отличается от «неопределенного поведения», но все равно означает, что вы не можете с пользой применять операторы, поскольку разные реализации могут иметь разные результаты.«Unspecified» в основном означает, что реализация получает определение поведения.

1 голос
/ 12 июля 2011

Хотя описание становится немного длинным, как насчет наличия манекена переменная и сравнивая ее указатель следующим образом?

template< class T >
struct comparable_value {
    T value;
    char *id;

    comparable_value( T value, char* id ) : value( value ), id( id ) {}

    bool operator<( comparable_value const& x ) const {
        return std::less< char* >()( id, x.id );
    }
};

template< class T, T V >
comparable_value< T > get_comparable_value() {
    static char dummy;
    return comparable_value< T >( V, &dummy );
}

struct A {
    void f() { puts( "f" ); }
    void g() { puts( "g" ); }
};

int main() {
    typedef void (A::*MF)();
    typedef std::set< comparable_value< MF > > set_t;
    set_t s;
    s.insert( get_comparable_value< MF, &A::f >() );
    s.insert( get_comparable_value< MF, &A::g >() );
    A a;
    for ( set_t::iterator i = s.begin(), e = s.end();  i != e;  ++ i )
        (a.*i->value)();
}

Вот тест на ideone .

1 голос
/ 12 июля 2011

5.9.7 (реляционные операторы): «Другие сравнения указателей не определены».

Так как 5.9 неясен (он имеет дело с функциями, но не является явно функциями-членами), быстрый взгляд на 5.10 (сравнение равенства) четко отделяет функции от функций-членов :

Кроме того, можно сравнивать указатели на элементы или указатель на член и константа нулевого указателя. Указатель на членство преобразований (4.11) и квалификационные преобразования (4.4) выполняются, чтобы привести их к общему типу. Если один операнд является константой нулевого указателя, общий тип - это тип другого операнда. В противном случае, общее тип - указатель на тип члена, аналогичный (4.4) типу одного из операнды с квалификационной подписью cv (4.4), которая является объединение cv-квалификационных сигнатур типов операндов. [Заметка: это означает, что любой указатель на член можно сравнить с нулевым константа указателя. ] Если оба операнда равны нулю, они сравниваются одинаково. В противном случае, если только один равен нулю, они сравниваются неравно. В противном случае, если либо указатель на виртуальную функцию-член, результат неопределенные. В противном случае они сравниваются равными, если и только если они относятся к тому же члену того же самого производного объекта (1.8) или тот же подобъект, если они были разыменованы с гипотетическим объектом связанный тип класса.

Таким образом, вы можете использовать операторы, значение == и != указано, но значение <, >, <= и >= не указано.

В частности, ничего не навязывает транзитивность, поэтому неясно, нормально ли их помещать в набор.

1 голос
/ 12 июля 2011

Если вы просто хотите произвольно упорядочить их в качестве ключей в наборе / карте, вы можете reinterpret_cast их.Вам может понадобиться шаблонный класс, такой как exact_int<sizeof(void (Foo::*bar)())>::type, потому что указатели на функции-члены могут иметь смешные размеры .

0 голосов
/ 12 июля 2011

Вы можете сделать что-то подобное идее Исе, только сохранить ее в классе Callback2, чтобы вам не нужно было менять что-либо, использующее класс.

template <class Receiver, class Sender>class CallBack2 : public ICallBack2 {
private:
   static int nextSequenceNumber;
   int sequenceNumber;

//snip

public:
  CallBack2(Receiver* _receiver, void(Receiver::*_function)(Sender*), Sender* _sender) :
   sequenceNumber(nextSequenceNumber++), receiver(_receiver), function(_function), sender(_sender) {};

//snip

virtual bool operator<(const ICallBack2* _other) const {
  return sequenceNumber<_other->sequenceNumber;}
0 голосов
/ 12 июля 2011

Нет смысла сравнивать два указателя функций. На самом деле вы можете сравнить возвращаемое значение этих функций:

*function(sender) < *(other->function)(sender)

но в вашем случае вы объявляете функцию как:

void(Receiver::*function)(Sender*);

так, на мой взгляд. Сравнение функций бесполезно. Либо измените сигнатуру функции, чтобы она возвращала что-то, либо лучше опишите ваш бизнес-сценарий, чтобы мы могли лучше понять, что вы хотите.

0 голосов
/ 12 июля 2011

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

    class a 
    {
    public:
        virtual void test();
    };
    class b
    {
    public:
        virtual void test();
    };

    ....
    a *pa = new a;
    b *pb = new b;

    if (pb->test == pa->test) // legal, but  false
    if (pb->test==pb::test) // legal and true
    // pa->test will evaluate to a::test, although calling pa->test() would call
    // b::test()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...