C++中的std::set如何实现元素唯一性覆盖

5
std::set<T>容器如何检查两个对象是否唯一?我尝试重载等号运算符(==),但没有成功。我想这样做的原因是,假设我有一个类Person,并且我指定如果他们有相同的姓名(甚至出生日期、地址等),则我的Person是同一个人。在 ccpreference.com 中,他们写道(对我来说有点不清楚):在标准库使用比较概念时,“唯一性”通过使用等价关系来确定。简单地说,如果没有一个对象小于另一个对象,则认为两个对象a和b是等价的(不唯一):!comp(a, b) && !comp(b, a)。我认为,这个问题也扩展到其他STL容器,甚至算法(甚至整个STL)。所以,如果将来我想使用函数std::find,我会查找该人的姓名而不是对象本身。这正确吗?

编辑

我想添加一些示例代码。

// My operator overloading comparing two strings.
bool operator==(Node & rhs) const {
        return this->name.compare(rhs.name);
}

然后,在 UnitTest 中,我将具有相同名称的对象两次添加到集合中。它被添加了两次(但根据 operator== 应该是相同的)。

void test_adding_two_identical_nodes() {
    // The pool is a set<Node> inside
    model::Node_Pool pool{};
    pool.store_node(model::Node{"Peter"});
    pool.store_node(model::Node{"Peter"});
    // Should be only 1 because the same node should be added once into a set.
    ASSERT_EQUAL(1, pool.size());
}

2
http://en.cppreference.com/w/cpp/container/set - juanchopanza
你写道“它没有起作用”,能否给我们展示一下(最小化的)代码示例,说明哪里出了问题? - max66
使用用户定义类型的std::set,如何确保没有重复项 - NathanOliver
@max66 让我在一秒钟内添加它。 - El Mac
此外,请参见此页面上的“关系运算符”:http://en.cppreference.com/w/cpp/language/operators - Robᵩ
2个回答

4

std::set<T> 不使用 == 来进行比较。默认情况下,它使用 std::less<T> 进行比较。而 std::less<T> 默认情况下使用运算符 < 进行比较。

实现 set 的一种方式是重载运算符 <,如下所示:

#include <set>
#include <cassert>

struct Person {
    const char *name;
    int uid;
};
bool operator<(const Person& a, const Person& b) {
    return a.uid < b.uid;
}
int main () {
   Person joe = {"joseph", 1};
   Person bob = {"robert", 2};
   Person rob = {"robert", 3};
   Person sue = {"susan", 4};

   std::set<Person> people;
   people.insert(joe);
   people.insert(bob);
   people.insert(rob);

   assert(people.count(joe) == 1);
   assert(people.count(bob) == 1);
   assert(people.count(rob) == 1);
   assert(people.count(sue) == 0);

   Person anonymous_3 = {"", 3};
   assert( std::strcmp(people.find(anonymous_3)->name, "robert") == 0);
}

另外,当声明set时,可以将比较运算符作为模板参数传递。在上面的示例中,这可能是比较运算符:

struct Person_Compare {
    bool operator()(const Person& a, const Person& b) const {
        return a.uid < b.uid;
    }
};

std::set 的声明可能是这样的:

std::set<Person, Person_Compare> people;

剩下的例子没有改变。

如果我使用std::find函数,这个方法也能行得通吗?它会找到两个具有相同uid的对象吗? - El Mac
@ElMac:不需要。这需要使用operator==。但是你不会在set中使用std::find - Lightness Races in Orbit
@ElMac 我在示例中添加了对 std::set::find() 的调用。 - Robᵩ
@Robᵩ 如果我有一个 Personshared_ptr,并且我的 setset<shared_ptr<Person>>,会发生什么?如果 set 中的 Person 首先嵌套在 shared_ptr 中,我该如何找到一个人的名字呢?我的意思是我想做类似于 my_set.find("STRING"); 的操作。 - El Mac

0
首先,不要覆盖比较运算符来比较除了完全等价之外的任何东西。否则,你最终会陷入维护噩梦中。
话虽如此,你应该覆盖 operator <。不过,你最好为集合设置一个比较器类型。
struct compare_people : std::binary_function<person,person,bool>
{
    bool operator () ( person const& a, person const& b) const { return a.name() < b.name();
};

std::set<person, compare_people> my_set;

4
std::binary_function 已被弃用,不应再使用。 - NathanOliver
1
@NathanOliver - std::binary_function已被弃用,因为有更新的样式可以完成不需要引入其名称的操作,但是在编写旧风格比较器时使用它没有问题。 - Pete Becker
2
@PeteBecker 我的警告是因为这被标记为C++14。 - NathanOliver
1
@NathanOliver - 非正常顺序。 - Pete Becker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接