c++ 使用结构作为键的Map工作不正常

pcww981p  于 2023-05-30  发布在  其他
关注(0)|答案(1)|浏览(83)

我尝试使用用户定义的结构体作为std::map中的键。为了做到这一点,我在结构中定义了比较运算符。然后我将类的两个示例添加到map中。

#include <map>
#include <iostream>

struct Fraction {
    int num, den; // numerator and denumenator

    explicit Fraction() : num(1), den(1) {}
    explicit Fraction(int num_, int den_) : num(num_), den(den_) {}

    bool operator<(const Fraction& rhs) const {
        return num * rhs.den < den * rhs.num;
    }

};

int main() {
    std::map<Fraction, int> mymap;
    
    mymap[Fraction(100, 100)] = 1;
    mymap[Fraction(200, 200)] = 2;
    
    std::cout << mymap.at(Fraction(100, 100)) << std::endl;
    std::cout << mymap.at(Fraction(200, 200)) << std::endl;
}

我希望得到

1
2

但结果是

2
2

为什么?

to94eoyn

to94eoyn1#

如果a < bb < a都不为真,则map认为两个 Keys 相等。

mymap[Fraction(100, 100)] = 1;
mymap[Fraction(200, 200)] = 2;

第一个分数是100/100,第二个分数是200/200100/100 < 200/200200/200 < 100/100在数学上都不正确。但是,您没有使用整数除法来进行比较,所以让我们检查num * rhs.den < den * rhs.num;100*200 < 200*100,这也适用于那里。

  • 100*200 < 200*100200*100 < 100*200都不为真,所以它们被认为是相等的。

这就是为什么你在mymap[Fraction(200, 200)] = 2;中覆盖了mymap[Fraction(100, 100)] = 1;存储的值,并且你的Map中只有一个 Key 分数。
我注意到你在评论中说 “我在operator<中添加了额外的检查,以区分这两种情况”。这可能是一个错误,因为它会打破Fraction类用户的期望。考虑以下代码片段:

Fraction a(100, 100);
Fraction b(200, 200);

if(a < b) {
    // should not happen
} else if(b < a) {
    // should not happen
} else {
    // the expected
}

如果您修改了operator<,使其能够在map中同时使用ab作为 Keys,则两个 “should not happen” 情况中的一个将启动。
备选方案:

  • 使用std::multimap,其中可以存储多个被视为相等的 * Key *。但是,这将更改您的Map,以便可以存储多个100/100Keys,而不仅仅是100/100200/200
  • 使用带有自定义比较器的std::map
struct FractionCmp {
    bool operator()(const Fraction& lhs, const Fraction& rhs) const {
        if(lhs < rhs) return true;
        if(rhs < lhs) return false;
        // they are really equivalent, but:
        return lhs.num < rhs.num;
    }
};

std::map<Fraction, int, FractionCmp> mymap;

相关问题