【问题标题】:Define generic comparison operator定义通用比较运算符
【发布时间】:2017-08-09 11:36:35
【问题描述】:

我想出了一个想法来定义一个通用的比较运算符,它适用于任何类型,为了它的乐趣。

#include <cstring>
#include <iostream>

class A
{
    public:
        A(int id) : id(id) {}

    private:
        int id;
};

template <class T>
inline bool operator==(const T& a, const T& b)
{
    return memcmp(&a, &b, sizeof(a)) == 0; // implementation is unimportant (can fail because of padding)
}

int main()
{
    std::cout << (A(10) == A(10)) << std::endl; // 1
    std::cout << (A(10) == A(15)) << std::endl; // 0
}

我认为这可能有助于解决 c++ 中缺少默认比较运算符的问题。

这是一个糟糕的主意吗?我想知道这样做是否会在某些情况下破坏任何东西?

【问题讨论】:

  • “实施不重要” 不,不是。它将编译错误转换为运行时错误。这很糟糕。
  • 这与 reinterpret_cast 一样糟糕,无法消除编译器错误。没有定义比较运算符的类型出于各种原因这样做。你用正确的、定义明确的代码来代替一些很少有用的语法糖。
  • @Bathsheba 提示某人为他们的类型重载了一元 operator&amp;
  • 如果您的对象中有引用或指针,您将比较地址而不是值
  • 而且具有可变成员的类也将受到怀疑。由于const 方法可以更改可变成员,但不应使相等对象不相等,因此相等不能依赖于那些mutable 成员。

标签: c++ generics


【解决方案1】:

这样做确实是个糟糕的主意。

如果某些类型没有定义相等运算符,很可能是因为您无法合理地比较该类型的两个对象是否相等。

即使缺少的相等运算符是实施者的疏忽,您想出的任何“包罗万象”的实施非常不太可能做一些明智的事情。

因此得出结论:不要这样做!编译时错误比运行时错误要好;与其过早地添加隐藏实际问题的最确定的“解决方案”,不如在发生编译时错误时添加实际解决方案。


对于初学者,您提出的解决方案对于带有填充的类型、带有重载一元 operator&amp; 的类型以及任何具有类似成员的指针或引用的类型都失败了;甚至具有上述任何类别的任何成员或基础的类型。所以对于 的东西。

【讨论】:

  • 您可能的意思是:所有没有标准布局的结构。
  • @BЈовић 实际上,没有。标准布局类型仍然可能有填充,这往往会破坏 memcmp 业务。
  • 我的意思是,所有非标准布局类都会立即失败。填充是需要考虑的额外事项。
  • @BЈовић 很公平。无论如何,重点是:休息一吨的东西。 :)
【解决方案2】:

让我们参加一个完全正常的课程,比如String。它的实现如您所想,char* 指向 new[]'ed 缓冲区。

现在比较其中两个。显然,String("abc")==String("abc")。然而,您的实现未能通过此测试,因为两个指针不同。

平等是由类语义定义的,而不是直接在对象内部的位。

【讨论】:

    【解决方案3】:

    是的,这是一个糟糕的主意:
    如果指针未初始化:
    这是一个失败示例(所以这段代码有两个不同的输出):

    #include <cstring>
    #include <iostream>
    
    class A {
    public:
      A(int id) : id(id) {}
    
    private:
      int id;
      A* a;
    };
    
    template <class T> inline bool operator==(const T &a, const T &b) {
      return memcmp(&a, &b, sizeof(a)) == 0;
    }
    
    int main() {
      std::cout << (A(10) == A(10)) << std::endl; // 1
      std::cout << (A(10) == A(15)) << std::endl; // 0
    }
    

    输出:

    0
    0
    

    并且两个指针的两个相同值的 RAM 初始内容的可能性极小,那么另一个输出是:

    1
    0
    

    【讨论】:

    • 实际上,您的示例具有未定义的行为,因为 A 的成员 a 未初始化。不保证输出两个零值。
    【解决方案4】:

    顺便说一句,我所知道的为有很多成员的类编写相等运算符的最好方法就是使用这个想法(此代码需要 C++ 14):

    #include <tuple>
    
    struct foo
    {
        int x    = 1;
        double y = 42.0;
        char z   = 'z';
    
        auto
        members() const
        {
            return std::tie(x, y, z);
        }
    };
    
    inline bool
    operator==(const foo& lhs, const foo& rhs)
    {
        return lhs.members() == rhs.members();
    }
    
    int
    main()
    {
        foo f1;
        foo f2;
    
        return f1 == f2;
    }
    

    Code on Compiler Explorer

    【讨论】:

      猜你喜欢
      • 2021-11-27
      • 1970-01-01
      • 1970-01-01
      • 2019-11-26
      • 2021-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多