为什么我必须在 POD 类型中重载运算符==

Why do I have to overload operator== in POD types?

本文关键字:重载 运算符 类型 POD 为什么      更新时间:2023-10-16

我有一个结构,定义如下:

struct Vec3 {
float x, y, z;
}

当我尝试在std::vector<Vec3>上使用std::unique时,我遇到了这个错误:

说明资源路径位置类型 与"_first"中的"运算符=="不匹配_gnu_cxx::__normal_iterator<_Iterator, _Container>::运算符* 其中 _Iterator = Vec3*, _Container = std::vector> == _next._gnu_cxx::__normal_iterator<_Iterator, _Container>::运算符* _Iterator = Vec3*, _Container = std::vector>' ModelConverter 行 4351,外部位置:/usr/include/c++/4.4.6/bits/stl_algo.h C/C++ 问题

我理解编译器在相等运算符和其他运算符天真的必要性(在这种情况下,* 几乎肯定不是我的意思(,但这是一个政策问题,还是有我不知道的技术原因? 有一个默认的赋值运算符,那么为什么没有默认的相等运算符呢?

没有技术原因。从学究的角度来看,你可能会说这是因为 C 不允许你用 == 比较两个结构,这是一个很好的理由;当您转到C++时,这种行为切换并不明显。(据推测,C 不支持的原因是字段比较可能适用于某些结构,但绝对不是全部。

仅从C++的角度来看,如果您拥有私人领域怎么办?默认==在技术上公开该字段(间接,但仍然如此(。那么,如果没有私有或受保护的数据成员,编译器是否只会生成operator==

此外,有些类没有合理的相等定义(空类、不对状态进行建模但缓存状态的类等(,或者默认的相等性检查可能非常令人困惑(包装指针的类(。

然后是继承。在继承情况下决定operator==做什么很复杂,编译器很容易做出错误的决定。(例如,如果这是C++所做的,我们可能会收到关于为什么==测试两个对象之间的相等性的问题,这两个对象都是抽象基类的后代并用于

引用它。

基本上,这是一个棘手的问题,编译器远离它更安全,即使考虑到你可以覆盖编译器决定的任何内容。

为什么你必须提供operator==的问题与为什么你必须提供一些比较函数的问题不同。

关于后者,需要您提供比较逻辑的原因是元素相等很少合适。例如,考虑一个包含char数组的 POD 结构。如果它用于保存以零结尾的字符串,则两个这样的结构可以在二进制级别比较不相等(由于字符串中零字节之后的任意内容(,但在逻辑上是等效的。

此外,这里还有其他答案提到的所有C++级复杂性,例如特别棘手的多态相等(你真的不希望编译器选择!

所以,

从本质上讲,根本没有好的默认选择,所以选择是你的。

关于前一个问题,也就是你从字面上问的,你为什么要提供operator==

如果定义 operator<operator== ,则命名空间 std::rel_ops 中的运算符定义可以为您填充其余部分。据推测,需要operator==的原因是,在operator<方面实现它会不必要地效率低下(然后需要两个比较(。然而,选择这两个运算符作为基础是完全令人困惑的,因为它使用户代码冗长和复杂,在某些情况下效率远低于可能的效率!

恕我直言,比较运算符的最佳基础是三值compare函数,例如 std::string::compare .

给定一个成员函数变体comparedTo,然后您可以使用如下所示的奇怪重复模板模式类来提供完整的运算符集:

template< class Derived >
class ComparisionOps
{
public:
    friend int compare( Derived const a, Derived const& b )
    {
        return a.comparedTo( b );
    }
    friend bool operator<( Derived const a, Derived const b )
    {
        return (compare( a, b ) < 0);
    }
    friend bool operator<=( Derived const a, Derived const b )
    {
        return (compare( a, b ) <= 0);
    }
    friend bool operator==( Derived const a, Derived const b )
    {
        return (compare( a, b ) == 0);
    }
    friend bool operator>=( Derived const a, Derived const b )
    {
        return (compare( a, b ) >= 0);
    }
    friend bool operator>( Derived const a, Derived const b )
    {
        return (compare( a, b ) > 0);
    }
    friend bool operator!=( Derived const a, Derived const b )
    {
        return (compare( a, b ) != 0);
    }
};

其中compare是一个重载函数,例如:

template< class Type >
inline bool lt( Type const& a, Type const& b )
{
    return std::less<Type>()( a, b );
}
template< class Type >
inline bool eq( Type const& a, Type const& b )
{
    return std::equal_to<Type>()( a, b );
}
template< class Type >
inline int compare( Type const& a, Type const b )
{
    return (lt( a, b )? -1 : eq( a, b )? 0 : +1);
}
template< class Char >
inline int compare( basic_string<Char> const& a, basic_string<Char> const& b )
{
    return a.compare( b );
}
template< class Char >
inline int compareCStrings( Char const a[], Char const b[] )
{
    typedef char_traits<Char>   Traits;
    Size const  aLen    = Traits::length( a );
    Size const  bLen    = Traits::length( b );
    // Since there can be negative Char values, cannot rely on comparision stopping
    // at zero termination (this can probably be much optimized at assembly level):
    int const way = Traits::compare( a, b, min( aLen, bLen ) );
    return (way == 0? compare( aLen, bLen ) : way);
}
inline int compare( char const a[], char const b[] )
{
    return compareCStrings( a, b );
}
inline int compare( wchar_t const a[], wchar_t const b[] )
{
    return compareCStrings( a, b );
}

现在,这就是机器。将其应用于您的班级是什么样子的......

struct Vec3
{
    float x, y, z;
};

嗯,这很简单:

struct Vec3
    : public ComparisionOps<Vec3>
{
    float x, y, z;
    int comparedTo( Vec3 const& other ) const
    {
        if( int c = compare( x, other.x ) ) { return c; }
        if( int c = compare( y, other.y ) ) { return c; }
        if( int c = compare( z, other.z ) ) { return c; }
        return 0;   // Equal.
    }
};

免责声明:不是经过严格测试的代码... :-(

C++20 添加了此功能:

struct Vec3 {
    float x, y, z;
    auto operator<=>(const Vec3&) const = default;
    bool operator==(X const&) const = default;
}

这目前仅在 GCC 和 clang 主干中实现。请注意,当前默认operator<=>等同于默认operator==,但是有一个公认的建议来删除它。该提案建议将违约operator<=>也意味着(不等同于今天(将违约operator==作为扩展。

Microsoft https://devblogs.microsoft.com/cppblog/simplify-your-code-with-rocket-science-c20s-spaceship-operator/有关于此功能的文档。

您希望相等运算是什么?所有字段都一样?它不会为你做出这个决定。