Параметр-ключ в map в виде структуры/класса
От: eklmn  
Дата: 01.03.02 16:47
Оценка:
Сначала о предмете вопроса. Есть вот такая программа:


#pragma warning(disable: 4786)

#include <iostream>
#include <map>
#include <algorithm>
#include <string>
#include <utility>

using namespace std;


class CKey {
public:
   bool operator<(const CKey& right) const {
       if (f1<right.f1 && f2<right.f2)
          return true;
       else
          return false;
   }
   bool operator==(const CKey &right) const {
       if (f1==right.f1 && f2==right.f2)
          return true;
       else
          return false;  
   }
   /*CKey& operator=(const CKey& right) {
        f1=right.f1;
        f2=right.f2;
        return *this;
   }*/
   CKey(){}
   CKey(int a, int b) : f1(a), f2(b) {}
public:
   int f1;
   int f2;
};

class CCompare {
   bool operator() (CKey& t1,CKey t2) {
       if (t1==t2)
           return true;
       else
           return false;
   }
};

typedef multimap<CKey, int> tagData;
tagData data;

void main()
{
     for(int i=1; i<=10; i++)
        data.insert(make_pair(CKey(i,i), i));
     
     tagData::iterator it;
     
     for(it=data.begin(); it!=data.end(); ++it)
        cout << it->first.f1 << "." << it->first.f2 << endl;
     
     CKey k(-6,6);
     it=data.find(k);                                //(1)
     //it=find(data.begin(), data.end(), k);         //(2)

     if (it==data.end())
         cout << "Not find" << endl;
     else
         cout << "Find" << endl;
      
}


В таком виде она выдает, что ключ найден. Почему?
Если CKey(6,-6), то ключ также найден, а если CKey(-6,-6), то не найден.

На вызов (2) компилятор (VC6) выдает ошибку
c:\program files\microsoft visual studio\vc98\include\algorithm(43) : error C2784:
'bool __cdecl std::operator ==(const class std::basic_string<_E,_Tr,_A> &,const _E *)'
: could not deduce template argument for 'const class std::basic_string<_E,_Tr,
_A> &' from 'struct std::pair<class CKey const ,int>' Почему?
Что я сделал не так?

Спасибо за помощь.
Re: Параметр-ключ в map в виде структуры/класса
От: DarkGray Россия http://blog.metatech.ru/post/ogni-razrabotki.aspx
Дата: 01.03.02 17:11
Оценка:
Здравствуйте eklmn, Вы писали:

E>Сначала о предмете вопроса. Есть вот такая программа:


E>

E>#pragma warning(disable: 4786)

E>#include <iostream>
E>#include <map>
E>#include <algorithm>
E>#include <string>
E>#include <utility>

E>using namespace std;


E>class CKey {
E>public:
E>   bool operator<(const CKey& right) const {
E>       if (f1<right.f1 && f2<right.f2)
E>          return true;
E>       else
E>          return false;
E>   }
E>   bool operator==(const CKey &right) const {
E>       if (f1==right.f1 && f2==right.f2)
E>          return true;
E>       else
E>          return false;  
E>   }
E>   /*CKey& operator=(const CKey& right) {
E>        f1=right.f1;
E>        f2=right.f2;
E>        return *this;
E>   }*/
E>   CKey(){}
E>   CKey(int a, int b) : f1(a), f2(b) {}
E>public:
E>   int f1;
E>   int f2;
E>};

E>class CCompare {
E>   bool operator() (CKey& t1,CKey t2) {
E>       if (t1==t2)
E>           return true;
E>       else
E>           return false;
E>   }
E>};

E>typedef multimap<CKey, int> tagData;
E>tagData data;

E>void main()
E>{
E>     for(int i=1; i<=10; i++)
E>        data.insert(make_pair(CKey(i,i), i));
E>     
E>     tagData::iterator it;
E>     
E>     for(it=data.begin(); it!=data.end(); ++it)
E>        cout << it->first.f1 << "." << it->first.f2 << endl;
E>     
E>     CKey k(-6,6);
E>     it=data.find(k);                                //(1)
E>     //it=find(data.begin(), data.end(), k);         //(2)

E>     if (it==data.end())
E>         cout << "Not find" << endl;
E>     else
E>         cout << "Find" << endl;
E>      
E>}

E>


E>В таком виде она выдает, что ключ найден. Почему?

E>Если CKey(6,-6), то ключ также найден, а если CKey(-6,-6), то не найден.
Для операции меньше должны выполняться все условия, как для математической операции меньше (транзитивность, коммутативность и т.д.)
В частности (если a != b), то (a <b) == !(b < a)
А у тебя:
(1, 0) < (0, 1) — true
(0, 1) < (1, 0) — true

Поменяй лучше операцию меньше на следующее
if (f1<right.f1)
  return true;
if (right.f1 < f1)
  return false;
if (f2 < right.f2)
  return true;
if (right.f2 < f2)
  return false;
return true;
Re: Параметр-ключ в map в виде структуры/класса
От: Андрей Тарасевич Беларусь  
Дата: 01.03.02 17:47
Оценка:
Здравствуйте eklmn, Вы писали:

E>Сначала о предмете вопроса. Есть вот такая программа:



E>class CKey {

E>public:
E> bool operator<(const CKey& right) const {
E> if (f1<right.f1 && f2<right.f2)
E> return true;
E> else
E> return false;
E> }

E>typedef multimap<CKey, int> tagData;

E>tagData data;


E> CKey k(-6,6);

E> it=data.find(k); //(1)

E>В таком виде она выдает, что ключ найден. Почему?

E>Если CKey(6,-6), то ключ также найден, а если CKey(-6,-6), то не найден.

Если я возьму ключи CKey(6, -6) и CKey(-6, 6) и попробую сравнить их при помощи твоего твоего оператора '<', то я получу, что

CKey(6, 6) < CKey(6, -6) => false
CKey(6, -6) < CKey(6, 6) => false

Т.е. ни первый не меньше, ни второй не меньше. Это означает, что, согласно твоему оператору сравнения, CKey(6, 6) и CKey(6, -6) — эквивалентны. Поэтому, когда ты просишь найти CKey(6, -6), находится CKey(6, 6). Сделай нормальный оператор сравнения '<' и все заработает.

E> //it=find(data.begin(), data.end(), k); //(2)


E> if (it==data.end())

E> cout << "Not find" << endl;
E> else
E> cout << "Find" << endl;
E>
E>}

E>[/ccode]


E>На вызов (2) компилятор (VC6) выдает ошибку

E>c:\program files\microsoft visual studio\vc98\include\algorithm(43) : error C2784:
E>'bool __cdecl std::operator ==(const class std::basic_string<_E,_Tr,_A> &,const _E *)'
E> : could not deduce template argument for 'const class std::basic_string<_E,_Tr,
_A>> &' from 'struct std::pair<class CKey const ,int>' Почему?
E>Что я сделал не так?

В твоем 'std::map' хранятся элементы типа 'std::pair<class CKey const ,int>'. Именно такой аргумент надло передавать в 'find'. Но в использовании 'std::find' для поиска по ключу в 'std::map' — это бессмыслица. Исаправь свой оператор '<' и пользуйся 'std::map::find'
Best regards,
Андрей Тарасевич
Re: Параметр-ключ в map в виде структуры/класса
От: Kaa Украина http://blog.meta.ua/users/kaa/
Дата: 02.03.02 08:49
Оценка:
Здравствуйте eklmn, Вы писали:
E>class CKey {
E>public:
E> bool operator<(const CKey& right) const {
E> if (f1<right.f1 && f2<right.f2)
E> return true;
E> else
E> return false;
E> }
E> bool operator==(const CKey &right) const {
E> if (f1==right.f1 && f2==right.f2)
E> return true;
E> else
E> return false;
E> }
E> CKey(){}
E> CKey(int a, int b) : f1(a), f2(b) {}
E>public:
E> int f1;
E> int f2;
E>};

Попробуем интерпретировать товою ситуацию:
Представим себе множество, единицами которого являются объекты класса CKey. CKey ставится в соответствие 2 целых числа. Забудем на секунду о том, что диапазон int в С++ — конечен. Таким образом, рассматривается множество всех точек на плоскости, имеющих целые координаты. Так?
Дальше. Так как координаты точко на плоскости — целые, рискну предположить, что это именно координаты точки на плоскости. Тогда, операцию сравнения можно определить как сравнение расстояний от точек до нуля (точки с координатами (0,0).
Формула: sqrt(f1*f1 + f2*f2) — расстояние от точки до начала координат, или модуль.

Это — не очень хорошая операция в таком случае, т.к. все точки, которые лежат на одинаковом удалении от центра — будут отождествляться. Что и продемонстрировано Андреем Тарасевичем рядом.

Но введенная тобой операция еще мене точная.
Она считает, что для точки A(xa,ya) все точки, лежащие в круге радиуса строго меньшего минимума 2-х координат min(xa,ya) — меньше ее, а все остальные — не меньше. Посему, не только приведенный тобой пример не работает, т.к. контейнер содержит точки A(6,-6) и B(-6,6), которые хоть и не удовлетворяют опреатору ==, но зато дают одинаковые результаты: A<B, B<A => A==B

В твоей же формуле A(xa,ya) < B(xb,yb) <=> max(xa,xb) < min(xb,yb)
От такой стратегии сравнения кто угодно с ума сойдет :-))

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

С уважением
Алексей Кирдин
Re[2]: Параметр-ключ в map в виде структуры/класса
От: eklmn  
Дата: 04.03.02 08:09
Оценка:
Большое спасибо всем ответившим.
Но! Тот исходник который я привел не то, что мне реально было нужно.
По своей наивности я подумал, что этого примера будет достаточно,
а все остальное я сделаю по аналогии... :)
Проблема немного шире, а именно мне нужно иметь соответствие
одной структуре другой структуре. В общем случае, в структуре,
которая будет рассматриваться как ключ, количество полей может
меняться, но в каждом конкретном случае я буду знать сколько
их и какого они типа. Стоит еще сказать, что тип данных полей
в структуре-ключе не обязательно int. Часть полей могут быть int,
а другая часть, скажем, string. Так же понятно, что мне придется
в каждом случае писать функции поиск/вставка/удаление.

А спросить хочу вот что: как мне все это лучше сделать???
Разумеется, что исходников я даже и не жду. Был бы очень
благодарен за просто советы о том в каком направлении копать.
Спасибо.
Re[3]: Параметр-ключ в map в виде структуры/класса
От: Kaa Украина http://blog.meta.ua/users/kaa/
Дата: 04.03.02 08:29
Оценка:
Здравствуйте eklmn, Вы писали:

E>А спросить хочу вот что: как мне все это лучше сделать???

Нужна более точная постановка вопроса. Так оно все слишком на пальцах. Я сразу могу несколько интерпретаций написанному привести, и половина из них сразу неправильная.

1. Разное кол-во полей внутри одной структуры/класса — невозможно.
2. Возможно, но хранение и доступ к ним надо организовывать вручную, а не с помощью доступа по имени. Для этого нужно сделать внутри класса массивчик, который должен содержать некоторые последовательности байт, одни из которых будут содержать данные (читай — поля), а другие (обычно их делают заголовками к данным) — содержат информацию о данных: тип, если надо — размер, и т.д. Доступ к полям в общем случае осуществляется по индексу через какую-то функцию-член типа GetField, а в производных классах знание конкретного местоположения полей приводит к появлению методов типа GetSomeInfo, которые будут звать GetField предка. Для каждого потомка приделать опреации сравнения. Все. Но без подробностей.

С уважением
Алексей Кирдин
Re[3]: Параметр-ключ в map в виде структуры/класса
От: Кодт Россия  
Дата: 04.03.02 10:12
Оценка:
Здравствуйте eklmn, Вы писали:

E>Проблема немного шире, а именно мне нужно иметь соответствие

E>одной структуре другой структуре. В общем случае, в структуре,
E>которая будет рассматриваться как ключ, количество полей может
E>меняться, но в каждом конкретном случае я буду знать сколько
E>их и какого они типа. Стоит еще сказать, что тип данных полей
E>в структуре-ключе не обязательно int. Часть полей могут быть int,
E>а другая часть, скажем, string. Так же понятно, что мне придется
E>в каждом случае писать функции поиск/вставка/удаление.

E>А спросить хочу вот что: как мне все это лучше сделать???


Т.е., существует дерево классов — ключей, и дерево классов — значений.
Поскольку напрямую сделать pair<CKey, CValue> не получится (хотя бы из-за варьирующихся размеров объектов), то в map'е должны храниться некие свертки.

Для CValue и его потомков — это может быть указатель (потребуется следить за созданием-удалением!)

Для CKey — придется озаботиться универсальным сравнивателем.
1) если возможно — написать операторы сравнения всех классов со всеми (например, если объекты не содержат указатели и под-объекты, — можно побайтно сравнить экземпляры)
2) придумать хэш-функцию и перекрыть ее вычисление для каждого класса
3) двухступенчатый механизм сравнения:
  • объекты различных классов сравниваются по именам классов
  • в одном классе идет развернутое сравнение
    Подобный же прием можно применить и к хэш-функции, чтобы избежать коллизий (ложных совпадений) для разных классов (пользующихся разными алгоритмами).
    class CKey
    {
    virtual const char* classname() const = 0;
    virtual bool is_less(const CKey* pObject) const = 0;
    
    virtual WORD local_hash() const = 0;
    
    bool operator <(const CKey& object) const
      {
         int cmp_class = strcmp(classname(), object.classname());
         if(cmp_class < 0) return true;
         if(cmp_class > 0) return false;
         return is_less(object);
      }
    
    DWORD hash() const
      {
        WORD class_hash = Hash_Of_String(classname());
        WORD object_hash = local_hash();
        return MAKEDWORD(object_hash, class_hash);
      }
    };
    
    class CKey1: public CKey
    {
    virtual const char* classname() const { return "CKey1"; }
    virtual bool is_less(const CKey* pObject) const;
      {
        CKey1* pKey1 = dynamic_ptr_cast<CKey1*>(pObject);
        assert(pKey1 != NULL);
        // здесь идут сравнения...
      }
    ...
    }


    Далее, в map'е придется хранить либо указатели на ключи, либо (если мы имеем дело с хэш-функцией) ее значения.
  • Перекуём баги на фичи!
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.