【问题标题】:Using comparator for STL set使用比较器设置 STL
【发布时间】:2010-10-30 05:53:22
【问题描述】:

检查以下代码:

string toLowerCase(const string& str) {
    string res(str);
    int i;

    for (i = 0; i < (int) res.size(); i++)
        res[i] = (char) tolower(res[i]);

    return res;
}

class LeagueComparator
{
public:
    bool operator()(const string& s1, const string& s2)
    {
        return toLowerCase(s1) < toLowerCase(s2);
    }
};

int main()
{
    set<string, LeagueComparator> leagues;
    set<string, LeagueComparator>::iterator iter;

    leagues.insert("BLeague");
    leagues.insert("aLeague");    // leagues = {"aLeague", "BLeague"}
    leagues.insert("ALeague");

    for (iter = leagues.begin(); iter != leagues.end(); iter++)
        cout << *iter << endl;

    return 0;
}

输出是:

aLeague
BLeague

这让我很震惊。我认为(并期待)输出将是:

aLeague
ALeague
BLeague

在执行leagues.insert("ALeague"); 之前,leagues 包含"aLeague" 和"BLeague"。我的问题是,在执行leagues.insert("ALeague"); 时,为什么机器会处理"ALeague" == "aleague"?据我了解,leagues 中没有元素"ALeague"。所以"ALeague"应该插入leagues。比较器应该确定在哪里放置"ALeague"。

提前致谢。

PS:请不要因为我使用 C 风格转换而打我。 :P 我懒得输入static_cast。

【问题讨论】:

  • 感觉您必须努力执行 C++ 样式转换这一事实是存在 C++ 样式转换的主要原因之一——即您应该避免在 C++ 中进行任何类型的转换。在这种情况下,您应该完全删除强制转换,并改用正确的类型。 IE。而不是(int) res.size(),删除演员表并将i的类型更改为unsigned。
  • 另外,i 应该在循环中声明,而不是在循环之外。而在 C++ 中,toLowerCase 可能应该只调用 std::transform(str.begin(), str.end(), str.begin(), std::ptr_fun(tolower)) 而不是编写显式循环。
  • @Billy ONeal:谢谢。我需要习惯使用transform()。 toLowerCase 是我多年前写的。我想我当时不知道transform。我会更新我的代码库。

标签: c++ stl comparator


【解决方案1】:

感谢toLowerCase,您的比较器表示"aLeague" == "ALeague"。因为(根据您的比较器)"aLeague" &lt; "ALeague" == false 和 "ALeague" &lt; "aLeague" == false,它们必须是等价的。并且将等效元素插入集合中没有任何作用。

【讨论】:

  • +1。请注意,比较器不是建立相等性,而是建立等价性。 w.r.t.有区别。标准和 STL 文档。
【解决方案2】:

当您向集合中插入任何值时,对象会检查它是否已包含该值。您的 LeagueComparator 对象将 ALeague 与集合中已有的其他两个值进行比较。它确定现有值 aLeague 既不大于也不小于建议的新条目 (ALeague),因此它们必须相等,因此不会继续插入。该集合仅包含两个元素。这就是提供客户比较对象的全部意义所在,因此您可以控制该集合如何确定两个元素是否匹配。

【讨论】:

  • +1。请注意,为了与 STL 文档保持一致,此处“equal”的大多数用法应替换为“equivalent”。小于比较器无法建立相等性。
【解决方案3】:

鉴于您提供的比较器,“ALeague”确实等同于“aLeague”。

给定两个值,x 和 y,以及一个小于比较器 z:

  • 如果 z(x, y) 为真,则 x 小于 y
  • 如果 z(y, x) 为真,则 y 小于 x
  • 如果两者都不为真,则 x 等价于 y
  • 如果两者都为真,那么您的比较器损坏了。

【讨论】:

  • +1,但第三个项目符号 w.r.t 有轻微问题。 STL 文档。平等和等价之间是有区别的。小于比较器不能建立相等性,只能建立等价性。
  • @Billy ONeal:根据 STL 文档(我没有),equality 和 equivalence 的定义是什么?
  • @Donotalo:平等是使用平等比较器进行比较,或operator==。等价是小于比较器或operator&lt; 对于任一参数排序返回 false 的状态,如此处所指定。从概念上讲,这是equality comparable 和less than comparable 之间的区别。
  • 参见Effective STL Item #19:了解相等和等价的区别,了解更多细节。
【解决方案4】:

将您的LeagueComparator 替换为

class LeagueComparator
{
public:
    bool operator()(const string& s1, const string& s2)
    {
        return toLowerCase(s1) < toLowerCase(s2)   ||  
               !(toLowerCase(s2) < toLowerCase(s1))  &&  s1 < s2;
    }
};

【讨论】:

  • 这相当于根本不指定比较器。您的比较器在所有情况下都只返回s1 &lt; s2。 (因为s1 &lt; s2 暗示toLowerCase(s1) &lt; toLowerCase(s2))-1
  • 这不是真的。 s1="b",s2="A"。由于false || !true &amp;&amp; true = false,我的比较器返回false。 s1 &lt; s2 返回true。
猜你喜欢
  • 2021-03-17
  • 1970-01-01
  • 2020-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-31
相关资源
最近更新 更多