Как иметь набор структур в С++

У меня есть структура, которая имеет уникальный ключ. Я хочу вставить экземпляры этих структур в набор. Я знаю, что для этого < оператор должен быть перегружен, чтобы набор мог провести сравнение, чтобы выполнить вставку.

Не работает следующее:

#include <iostream>
#include <set>
using namespace std;
struct foo 
{
      int key;
};

bool operator<(const foo& lhs, const foo& rhs)
{
      return lhs.key < rhs.key;
}

set<foo> bar;

int main()
{
    foo *test = new foo;
    test->key = 0;
    bar.insert(test);
}

Ответ 1

Это может помочь:

struct foo
{
  int key;
};

inline bool operator<(const foo& lhs, const foo& rhs)
{
  return lhs.key < rhs.key;
}

Если вы используете пространства имен, хорошей практикой является объявление функции operator<() в том же пространстве имен.


Для полноты после вашего редактирования и, как указывали другие, вы пытаетесь добавить foo*, где ожидается foo.

Если вы действительно хотите иметь дело с указателями, вы можете обернуть foo* в класс интеллектуального указателя (auto_ptr, shared_ptr,...).

Но обратите внимание, что в обоих случаях вы теряете преимущество перегруженного operator<, который работает на foo, а не на foo*.

Ответ 2

struct Blah
{
    int x;
};

bool operator<(const Blah &a, const Blah &b)
{
    return a.x < b.x;
}

...

std::set<Blah> my_set;

Однако мне не нравится перегрузка operator<, если она не делает интуитивного смысла (действительно ли имеет смысл сказать, что один Blah меньше "другого Blah?). Если это не так, я обычно предоставляю функцию пользовательского сравнения:

bool compareBlahs(const Blah &a, const Blah &b)
{
    return a.x < b.x;
}

...

std::set<Blah,compareBlahs> my_set;

Ответ 3

Вы можете перегрузить operator < внутри класса, а также,

struct foo 
{
  int key;
  bool operator < (const foo &other) const { return key < other.key; }
};

В вашем вопросе, если вы хотите использовать set<foo> bar; как объявление, тогда вы должны вставить значение как,

bar.insert(*test);

Но это не будет хорошей идеей, поскольку вы делаете избыточную копию.

Ответ 4

см. ereOn ответ, это правильно.

Реальная проблема в вашем коде:

foo *test = new foo;
test->key = 0;
bar.insert(test);

Вы вставляете указатель в набор, а не в структуру. Измените insert на:

 bar.insert( *test );
 //          ^

EDIT: но тогда вам нужно delete foo, так как оно будет скопировано в set. Или просто создать его в стеке (с помощью set с указателями это не очень хорошая идея, потому что расположение будет "странным" - set будет сортироваться в соответствии с адресами указателей)

Ответ 5

Лучше всего сделать foo конструктор:

struct foo
{
    foo(int k) : key(k) { }
    int key;
};

Затем добавить, а не...

foo *test = new foo;
test->key = 0;
bar.insert(test);   // BROKEN - need to dereference ala *test
// WARNING: need to delete foo sometime...

... вы можете просто использовать:

bar.insert(foo(0));

Ответ 6

Проблема не в вашем наборе; это в вашем объекте test. Вы используете Java-стиль там. В С++ мы просто пишем:

set<foo> bar;

int main()
{
    foo test; // Local variable, goes out of scope at }
    test.key = 0;
    bar.insert(test); // Insert _a copy of test_ in bar.
}

Ответ 7

В С++ 11 мы можем использовать лямбда-выражения, я использовал этот способ, который похож на то, что было дано @Oliver.

#include <set>
#include <iostream>
#include <algorithm>

struct Blah
{
    int x;
};



int main(){

    auto cmp_blah = [](Blah lhs, Blah rhs) { return lhs.x < rhs.x;};

    std::set<Blah, decltype(cmp_blah)> my_set(cmp_blah);

    Blah b1 = {2};
    Blah b2 = {2};
    Blah b3 = {3};

    my_set.insert(b1);
    my_set.insert(b2);
    my_set.insert(b3);

    for(auto const& bi : my_set){
        std::cout<< bi.x << std::endl;
    }

}

демонстрация