使用比较器进行STL设置

时间:2010-10-30 05:53:22

标签: c++ stl comparator

检查以下代码:

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"?根据我的理解,"ALeague"中没有元素leagues。因此"ALeague"应插入leagues。比较器应确定放置"ALeague"的位置。

提前致谢。

PS:请不要因为使用C风格的演员而打我。 :P我懒得键入static_cast

4 个答案:

答案 0 :(得分:14)

由于toLowerCase,你的比较器说"aLeague" == "ALeague"。由于(根据您的比较器)"aLeague" < "ALeague" == false"ALeague" < "aLeague" == false,它们必须是等效的。将等效元素插入集合中不会做任何事情。

答案 1 :(得分:4)

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

答案 2 :(得分:3)

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

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

  • 如果z(x,y)为真,则x小于y
  • 如果z(y,x)为真,则y小于x
  • 如果两者都不成立,那么x相当于y
  • 如果两者都是真的,那么你的比较器就会破碎。

答案 3 :(得分:0)

将您的LeagueComparator替换为

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