【问题标题】:Iterator through std::set using find() won't work. What's going wrong?使用 find() 通过 std::set 的迭代器将不起作用。怎么了?
【发布时间】:2017-08-02 17:09:01
【问题描述】:

我正在尝试创建一个包含联系人列表的程序,用户可以在其中根据电话号码搜索联系人的姓名。很抱歉包含这么多代码,但理解我的问题是必要的:

#include <iostream>
#include <string>
#include <set>
using namespace std;

struct ContactItem
{
    string name;
    string phoneNumber;
    string displayAs;

    ContactItem(const string inName, const string inNumber) : name(inName), phoneNumber(inNumber)
    {
        displayAs = name + ": " + phoneNumber;
    }

    bool operator== (const ContactItem& searchParameter) const
    {
        return (this->phoneNumber == searchParameter.phoneNumber);
    }

    bool operator< (const ContactItem& compareResult) const
    {
        return (this->name < compareResult.name);
    }

    operator const char*() const
    {
        return displayAs.c_str();
    }
};

int main()
{
    //Initialize a set and populate it with contacts of type ContactItem
    set<ContactItem> contactBook;
    contactBook.insert(ContactItem("Sally", "123654864"));
    contactBook.insert(ContactItem("Joe", "8435102654"));
    contactBook.insert(ContactItem("Steve", "8135691234"));
    contactBook.insert(ContactItem("Alice", "8432489425"));

    //Search for a contact's name by only being given their number
    cout << "Please give the number of one contact whose name you would like to know: " << endl;
    string userNumber;
    getline(cin, userNumber);

    auto findNumber = contactBook.find(ContactItem("", userNumber));

    if (findNumber != contactBook.end())
        cout << "The name of the contact whose number matches the phone number given is: " << (*findNumber).name << endl;
    else
        cout << "Contact not found" << endl;

    return 0;
}

我的问题似乎总是与auto findNumber = contactBook.find(userNumber); 行有关。每次我运行此代码时,都会显示消息“未找到联系人”。我无法弄清楚我做错了什么。是我对operator==的重新定义吗?

以上代码的灵感来自于 Rao,Siddhartha。 Sams 每天一小时自学 C++。第 8 版,印第安纳波利斯,印第安纳州,Sams,2017 年。

【问题讨论】:

    标签: c++ iterator set


    【解决方案1】:

    您不想使用std::set::find 来执行此操作。 std::set::find 是寻找完全匹配,但您正在寻找部分匹配。 std::set::find 将只查看联系人的一个子集,因为它知道它们已排序。但是您需要检查所有联系人,因为其中任何一个都可以匹配电话号码。

    您需要的是来自&lt;algorithm&gt;std::find_ifstd::find_if 接受一个谓词,它是一个函数或类似函数的对象,可以告诉你这是否是正确的。

    首先,包括&lt;algorithm&gt;

    #include <algorithm>
    

    我们可以为谓词使用 lambda:

    auto findNumber =
      std::find_if(contactBook.begin(), contactBook.end(),
                   [&userNumber](const ContactItem &contact) {
                     return contact.phoneNumber == userNumber;
                   });
    

    如果您以前没有使用过 lambda,这可能看起来很奇怪。 lambda 就像一个有状态的无名函数。

    方括号[] 告诉编译器这是一个lambda。 &amp;userNumber 表示,在 lambda 的主体中,我们需要引用当前范围内的 userNumber 变量。 (这称为“引用捕获”。)

    括号括起一个类似函数的参数列表。 std::find_if 将在每个联系人上调用此 lambda,就好像它是一个常规函数一样,通过传入对联系人的引用。

    lambda 的主体(大括号{})是一个函数主体,它返回一个bool 来告诉使用传入的联系人是否符合我们的匹配条件。主体可以引用传入的参数以及从定义范围“捕获”的任何内容。在这种情况下,我们只关心联系人的电话号码是否与所需的电话号码匹配。

    【讨论】:

      【解决方案2】:

      正如std::set documentation 中所述,默认情况下它使用std::less,在这种情况下使用您提供的ContactItem::operator&lt;。当您通过name 字段比较结构时 - 该字段用于比较元素,所以基本上您正在尝试查找名称为空的联系人。您要么需要为此 std::set 指定不同的比较器,要么相应地更改 ContactItem::operator &lt;

      注意:与std::unordered_set 不同,std::set 不会以任何形式使用operator==,您也可以在文档中看到:

      std::set 是一个关联容器,它包含一个排序的集合 Key 类型的唯一对象。排序是使用键比较完成的 比较函数。

      std::less documentation 说:

      用于执行比较的函数对象。除非专业, 在类型 T 上调用 operator

      【讨论】:

      • 可能值得补充的是 operator&lt; 必须实现 strict weak ordering - 这是初学者经常出错的地方。
      • 这个答案都是正确的,但它并没有真正解决问题。更改比较器无助于查找部分匹配的项目。
      • @AdrianMcCarthy 如果您使用忽略名称的比较器并且只比较此std::set 的电话,它将解决问题。
      • @Slava,但它也从根本上改变了集合包含的内容。使用 OP 的原始代码(仅检查名称),我们确保每个名称在集合中只出现一次,并且迭代集合为您提供按名称按字典顺序排列的项目。如果将比较器更改为电话号码,则每个号码只能在集合中出现一次,并且迭代该集合会给出按电话号码字符串按字典顺序排列的联系人。
      • @AdrianMcCarthy 根据逻辑,这个特定集合的存在是为了通过电话号码进行查找,所以我解释了如何修复这个特定程序中损坏的逻辑。我看不懂,如果 OP 需要更改字段来查找我的答案会有所不同,但 OP 必须指定。
      【解决方案3】:

      这里的问题是std::set 不使用您的operator == 来查找元素。相反,它使用与比较对象相同的东西operator &lt;。您要么必须更改 operator &lt; 以通过 phoneNumber 进行比较,要么提供指定联系人。

      您还可以考虑使用提升 multi-index container 来索引我的姓名和号码。

      【讨论】:

      • 多索引容器是一种解决方案,但简单地更改比较器会产生其他可能不受欢迎的后果(例如,两个联系人无法共享一个电话号码)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-04
      • 1970-01-01
      • 2017-07-27
      • 1970-01-01
      • 2018-11-15
      • 2011-07-15
      • 1970-01-01
      相关资源
      最近更新 更多