为什么用<运算符进行向量比较会比较每个项目两次?

时间:2018-07-05 09:17:04

标签: c++ operator-overloading stdvector

在此示例中,将两个向量与<运算符进行比较将得出Integer类上定义的运算符<,每个元素被调用两次。但是,将两个向量与==运算符进行比较时,不会发生这种情况。

List<MyInterface>

此代码显示:

#include<iostream>
#include<vector>

class Integer {
    public:
        Integer(int value) : m_value(value) {}
        friend bool operator<(const Integer& lhs, const Integer& rhs);
        friend bool operator==(const Integer& lhs, const Integer& rhs);

    private:
        int m_value;

}; 
bool operator<(const Integer& lhs, const Integer& rhs) {
            std::cout << lhs.m_value << " < " << rhs.m_value << '\n';
            return lhs.m_value < rhs.m_value;
}
bool operator==(const Integer& lhs, const Integer& rhs) {
            std::cout << lhs.m_value << " == " << rhs.m_value << '\n';
            return lhs.m_value == rhs.m_value;
}


int main()
{
    std::vector<Integer> ivec1 {1,2,3};
    std::vector<Integer> ivec2 {1,2,3};
    std::cout << (ivec1 < ivec2) << '\n';
    std::cout << (ivec1 == ivec2) << std::endl;
    return 0;
}

为什么会这样?

3 个答案:

答案 0 :(得分:10)

如果a < b返回false,它不会告诉您是否b < a,您必须进行测试。这是因为std::vector的逐元素排序可以为一对a, b元素产生三个结果:

  • a < b,向量比较将返回true
  • b < a,向量比较将返回false
  • 以上两个都不是,必须测试下一对元素。

因此必须比较两个方向。通过向班级添加标识数据,您可以更清楚地看到这一点:

#include<iostream>
#include<vector>

class Integer {
    public:
        Integer(int value, char tag) : m_value(value), m_tag(tag) {}
        friend bool operator<(const Integer& lhs, const Integer& rhs);
        friend bool operator==(const Integer& lhs, const Integer& rhs);

    private:
        int m_value;
        char m_tag;

}; 
bool operator<(const Integer& lhs, const Integer& rhs) {
            std::cout << lhs.m_value << ' ' << lhs.m_tag << " < " << rhs.m_value << ' ' << rhs.m_tag << '\n';
            return lhs.m_value < rhs.m_value;
}
bool operator==(const Integer& lhs, const Integer& rhs) {
            std::cout << lhs.m_value << ' ' << lhs.m_tag << " == " << rhs.m_value << ' ' << rhs.m_tag << '\n';
            return lhs.m_value == rhs.m_value;
}


int main()
{
    std::vector<Integer> ivec1 {{1, 'a'} ,{2, 'a'}, {3, 'a'}};
    std::vector<Integer> ivec2 {{1, 'b'} ,{2, 'b'}, {3, 'b'}};
    std::cout << (ivec1 < ivec2) << '\n';
    std::cout << (ivec1 == ivec2) << std::endl;
    return 0;
}

这将产生:

1 a < 1 b    
1 b < 1 a    
2 a < 2 b    
2 b < 2 a    
3 a < 3 b    
3 b < 3 a    
0    
1 a == 1 b    
2 a == 2 b    
3 a == 3 b    
1

[Live example]

答案 1 :(得分:4)

为了找到traces.append(go.Scatter( x=X, y=Y, text=feature, mode='markers', opacity=0.7, marker={'size': 15, 'line': {'width': 0.5, 'color': 'white'}}, name=feature)) ivec1的字典顺序,该实现查找第一个索引ivec2,其中iivec1[i] < ivec2[i]为这将确定顺序。

请注意,如果ivec2[i] < ivec1[i]为假,则需要进行两次比较。特别地,上述情况留下了两种可能性,即“ ivec1[i] < ivec2[i]ivec1[i]比较等效”和“ ivec2[i]”。这个决定是需要进行第二次比较的地方。


一旦找到这样的索引ivec2[i] < ivec1[i],实现就可以停止比较。但是在您的示例中,所有条目的比较结果都是相同的,因此必须对每对条目进行两次比较。

答案 2 :(得分:4)

这是由于C ++当前处理比较的设计存在缺陷。他们正在中修复它;我不知道它是否可以解决vector,但基本问题将得到解决。

首先是问题。

std::vector的{​​{1}}基于每个元素<。但是<不能胜任这项工作。

如果您有两个元素<a,要按字母顺序对元组b进行排序,您需要执行以下操作:

a,b

通常,如果要按字典顺序订购N个元素的集合,则需要对if (self.a < other.a) return true; if (other.a < self.a) return false; return self.b < other.b; 进行2N-1次调用。

这早已为人所知,这就是<返回具有3种值的整数的原因:strcmp表示较小值,-1表示相等值,而0大于(或通常小于或等于零的值)。

您可以这样做:

+1

这最多需要auto val = compare( self.a, other.a ); if (val != 0) return val; return compare( self.b, other.b ); 调用集合中的每个元素N

现在,解决方法。

添加了宇宙飞船比较运算符compare。它会返回一个可以比较的大于或小于零的类型,其确切类型会通告该操作提供的保证。

这类似于C的<=>,但可用于支持C的任何类型。此外,有strcmp个函数使用std(如果可用),否则使用<=><等来模拟它们。

假设将向量的要求重写为使用==,则带有<=>的类型将避免双重比较,而每个<=>最多仅进行一次<=>的字典顺序调用std::vector时为{1}}。