memcmp 与多重相等比较

2024-01-11

前提:考虑这样一个类或结构T,对于两个对象a and b类型的T

memcmp(&a, &b, sizeof(T)) == 0

产生相同的结果

a.member1 == b.member1 && a.member2 == b.member2 && ...

(memberN是一个非静态成员变量T).

Question: 什么时候应该memcmp用来比较a and b为了平等,什么时候应该链式==可以用吗?


这是一个简单的例子:

struct vector
{
    int x, y;
};

重载运算符== for vector,有两种可能性(如果保证给出相同的结果):

bool operator==(vector lhs, vector rhs)
{ return lhs.x == rhs.x && lhs.y == rhs.y; }

or

bool operator==(vector lhs, vector rhs)
{ return memcmp(&lhs, &rhs, sizeof(vector)) == 0; }

现在如果要添加一个新成员vector,例如z成分:

  • If ==s 用于实现operator==,必须对其进行修改。
  • If memcmp被用来代替,operator==根本不需要修改。

但我认为使用链式==s 表达了更明确的含义。虽然对于大T有很多成员memcmp更具有诱惑力。此外,使用是否有性能改进memcmp over ==是?还有什么要考虑的吗?


关于前提条件memcmp产生与成员方面的比较相同的结果==,虽然这个前提在实践中经常得到满足,但有点brittle.

理论上,更改编译器或编译器选项可以打破该前提条件。更值得关注的是,代码维护(所有编程工作的 80% 都是维护,IIRC)可以通过添加或删除成员、使类多态、添加自定义来破坏它==正如评论之一中提到的,前提条件可以适用于静态变量,但不适用于自动变量,然后创建非静态对象的维护工作可以做坏事™。

以及关于是否使用的问题memcmp或会员方面==实施一项==对于类的运算符,首先,这是一个错误的二分法,因为那些不是唯一的选择。

例如,它可以减少工作量并且使用起来更易于维护自动生成关系运算符重载,就compare功能。这std::string::comparefunction 是此类函数的一个示例。

其次,选择哪种实现的答案在很大程度上取决于您认为重要的内容,例如:

  • 是否应该寻求最大化运行时效率, or

  • 人们是否应该寻求创造最清晰的代码, or

  • 如果人们寻求最简洁的,写得最快代码,或

  • 应该努力让班级成为最safe使用,或

  • 也许是别的什么?

生成关系运算符。

您可能听说过 CRTP,奇怪的重复模板模式。我记得它是为了满足生成关系运算符重载的要求而发明的。不过,我可能会把它与其他东西混为一谈,但无论如何:

template< class Derived >
struct Relops_from_compare
{
    friend
    auto operator!=( const Derived& a, const Derived& b )
        -> bool
    { return compare( a, b ) != 0; }

    friend
    auto operator<( const Derived& a, const Derived& b )
        -> bool
    { return compare( a, b ) < 0; }

    friend
    auto operator<=( const Derived& a, const Derived& b )
        -> bool
    { return compare( a, b ) <= 0; }

    friend
    auto operator==( const Derived& a, const Derived& b )
        -> bool
    { return compare( a, b ) == 0; }

    friend
    auto operator>=( const Derived& a, const Derived& b )
        -> bool
    { return compare( a, b ) >= 0; }

    friend
    auto operator>( const Derived& a, const Derived& b )
        -> bool
    { return compare( a, b ) > 0; }
};

鉴于上述支持,我们可以调查您的问题可用的选项。

实现A:减法比较。

这是一个提供全套关系运算符的类,但不使用任何一个memcmp or ==:

struct Vector
    : Relops_from_compare< Vector >
{
    int x, y, z;

    // This implementation assumes no overflow occurs.
    friend
    auto compare( const Vector& a, const Vector& b )
        -> int
    {
        if( const auto r = a.x - b.x ) { return r; }
        if( const auto r = a.y - b.y ) { return r; }
        return a.z - b.z;
    }

    Vector( const int _x, const int _y, const int _z )
        : x( _x ), y( _y ), z( _z )
    {}
};

实施B:比较通过memcmp.

这是使用相同的类实现的memcmp;我想您会同意这段代码的扩展性更好并且更简单:

struct Vector
    : Relops_from_compare< Vector >
{
    int x, y, z;

    // This implementation requires that there is no padding.
    // Also, it doesn't deal with negative numbers for < or >.
    friend
    auto compare( const Vector& a, const Vector& b )
        -> int
    {
        static_assert( sizeof( Vector ) == 3*sizeof( x ), "!" );
        return memcmp( &a, &b, sizeof( Vector ) );
    }

    Vector( const int _x, const int _y, const int _z )
        : x( _x ), y( _y ), z( _z )
    {}
};

实施C:逐个成员进行比较。

这是使用成员比较的实现。它没有强加任何特殊要求或假设。但它更多的是源代码。

struct Vector
    : Relops_from_compare< Vector >
{
    int x, y, z;

    friend
    auto compare( const Vector& a, const Vector& b )
        -> int
    {
        if( a.x < b.x ) { return -1; }
        if( a.x > b.x ) { return +1; }
        if( a.y < b.y ) { return -1; }
        if( a.y > b.y ) { return +1; }
        if( a.z < b.z ) { return -1; }
        if( a.z > b.z ) { return +1; }
        return 0;
    }

    Vector( const int _x, const int _y, const int _z )
        : x( _x ), y( _y ), z( _z )
    {}
};

实施D:compare就关系运算符而言。

这是一种颠倒事物自然顺序的实现,通过实现compare按照< and ==,直接提供并根据std::tuple比较(使用std::tie).

struct Vector
{
    int x, y, z;

    friend
    auto operator<( const Vector& a, const Vector& b )
        -> bool
    {
        using std::tie;
        return tie( a.x, a.y, a.z ) < tie( b.x, b.y, b.z );
    }

    friend
    auto operator==( const Vector& a, const Vector& b )
        -> bool
    {
        using std::tie;
        return tie( a.x, a.y, a.z ) == tie( b.x, b.y, b.z );
    }

    friend
    auto compare( const Vector& a, const Vector& b )
        -> int
    {
        return (a < b? -1 : a == b? 0 : +1);
    }

    Vector( const int _x, const int _y, const int _z )
        : x( _x ), y( _y ), z( _z )
    {}
};

如所给出的,客户端代码使用例如>需要一个using namespace std::rel_ops;.

替代方案包括将所有其他运算符添加到上面(更多代码),或者使用 CRTP 运算符生成方案来实现其他运算符:< and =(可能效率低下)。

实施E:手动使用的比较< and ==.

这个实现是不应用任何抽象的结果,只是敲击键盘并直接写出机器应该做什么:

struct Vector
{
    int x, y, z;

    friend
    auto operator<( const Vector& a, const Vector& b )
        -> bool
    {
        return (
            a.x < b.x ||
            a.x == b.x && (
                a.y < b.y ||
                a.y == b.y && (
                    a.z < b.z
                    )
                )
            );
    }

    friend
    auto operator==( const Vector& a, const Vector& b )
        -> bool
    {
        return
            a.x == b.x &&
            a.y == b.y &&
            a.z == b.z;
    }

    friend
    auto compare( const Vector& a, const Vector& b )
        -> int
    {
        return (a < b? -1 : a == b? 0 : +1);
    }

    Vector( const int _x, const int _y, const int _z )
        : x( _x ), y( _y ), z( _z )
    {}
};

选择什么。

考虑最有价值的可能方面的列表,例如安全性、清晰度、效率、简短性,评估上述每种方法。

然后选择对您来说明显最好的一种方法,或者看起来同样最好的方法之一。

指导:为了安全起见,您不希望选择方法 A(减法),因为它依赖于对值的假设。请注意,还有选项 B,memcmp,作为一般情况的实现是不安全的,但可以很好地实现== and !=。为了效率你应该更好MEASURE,以及相关的编译器选项和环境,并记住 Donald Knuth 的格言:“过早的优化是万恶之源”(即花时间在这可能会适得其反)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

memcmp 与多重相等比较 的相关文章

  • 如何获取正在访问 ASP.NET 应用程序的当前用户?

    为了获取系统中当前登录的用户 我使用以下代码 string opl System Security Principal WindowsIdentity GetCurrent Name ToString 我正在开发一个 ASP NET 应用程
  • EF Core Group By 翻译支持条件总和

    听说 EF Core 2 1 将支持翻译小组 我感到非常兴奋 我下载了预览版并开始测试它 但发现我在很多地方仍然没有得到翻译分组 在下面的代码片段中 对 TotalFlagCases 的查询将阻止翻译分组工作 无论如何 我可以重写这个以便我
  • C 编程 - 文件 - fwrite

    我有一个关于编程和文件的问题 while current NULL if current gt Id Doctor 0 current current gt next id doc current gt Id Doctor if curre
  • GLKit的GLKMatrix“列专业”如何?

    前提A 当谈论线性存储器中的 列主 矩阵时 列被一个接一个地指定 使得存储器中的前 4 个条目对应于矩阵中的第一列 另一方面 行主 矩阵被理解为依次指定行 以便内存中的前 4 个条目指定矩阵的第一行 A GLKMatrix4看起来像这样 u
  • Web 客户端和 Expect100Continue

    使用 WebClient C NET 时设置 Expect100Continue 的最佳方法是什么 我有下面的代码 我仍然在标题中看到 100 continue 愚蠢的 apache 仍然抱怨 505 错误 string url http
  • 在结构中使用 typedef 枚举并避免类型混合警告

    我正在使用 C99 我的编译器是 IAR Embedded workbench 但我认为这个问题对于其他一些编译器也有效 我有一个 typedef 枚举 其中包含一些项目 并且我向该新类型的结构添加了一个元素 typedef enum fo
  • 堆栈溢出:堆栈空间中重复的临时分配?

    struct MemBlock char mem 1024 MemBlock operator const MemBlock b const return MemBlock global void foo int step 0 if ste
  • C#中如何移动PictureBox?

    我已经使用此代码来移动图片框pictureBox MouseMove event pictureBox Location new System Drawing Point e Location 但是当我尝试执行时 图片框闪烁并且无法识别确切
  • 显示UnityWebRequest的进度

    我正在尝试使用下载 assetbundle统一网络请求 https docs unity3d com ScriptReference Networking UnityWebRequest GetAssetBundle html并显示进度 根
  • 使用 Bearer Token 访问 IdentityServer4 上受保护的 API

    我试图寻找此问题的解决方案 但尚未找到正确的搜索文本 我的问题是 如何配置我的 IdentityServer 以便它也可以接受 授权带有 BearerTokens 的 Api 请求 我已经配置并运行了 IdentityServer4 我还在
  • 如何在 C 中调用采用匿名结构的函数?

    如何在 C 中调用采用匿名结构的函数 比如这个函数 void func struct int x p printf i n p x 当提供原型的函数声明在范围内时 调用该函数的参数必须具有与原型中声明的类型兼容的类型 其中 兼容 具有标准定
  • 什么时候虚拟继承是一个好的设计? [复制]

    这个问题在这里已经有答案了 EDIT3 请务必在回答之前清楚地了解我要问的内容 有 EDIT2 和很多评论 有 或曾经 有很多答案清楚地表明了对问题的误解 我知道这也是我的错 对此感到抱歉 嗨 我查看了有关虚拟继承的问题 class B p
  • 如何查看网络连接状态是否发生变化?

    我正在编写一个应用程序 用于检查计算机是否连接到某个特定网络 并为我们的用户带来一些魔力 该应用程序将在后台运行并执行检查是否用户请求 托盘中的菜单 我还希望应用程序能够自动检查用户是否从有线更改为无线 或者断开连接并连接到新网络 并执行魔
  • 使用 x509 证书签署 json 文档或字符串

    如何使用 x509 证书签署 json 文档或字符串 public static void fund string filePath C Users VIKAS Desktop Data xml Read the file XmlDocum
  • 对现有视频添加水印

    我正在寻找一种用 C 在视频上加水印的方法 就像在上面写文字一样 图片或文字标签 我该怎么做 谢谢 您可以使用 Nreco 视频转换器 代码看起来像 NReco VideoConverter FFMpegConverter wrap new
  • 如何从两个不同的项目中获取文件夹的相对路径

    我有两个项目和一个共享库 用于从此文件夹加载图像 C MainProject Project1 Images 项目1的文件夹 C MainProject Project1 Files Bin x86 Debug 其中有project1 ex
  • 基于 OpenCV 边缘的物体检测 C++

    我有一个应用程序 我必须检测场景中某些项目的存在 这些项目可以旋转并稍微缩放 更大或更小 我尝试过使用关键点检测器 但它们不够快且不够准确 因此 我决定首先使用 Canny 或更快的边缘检测算法 检测模板和搜索区域中的边缘 然后匹配边缘以查
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • Windows 和 Linux 上的线程

    我在互联网上看到过在 Windows 上使用 C 制作多线程应用程序的教程 以及在 Linux 上执行相同操作的其他教程 但不能同时用于两者 是否存在即使在 Linux 或 Windows 上编译也能工作的函数 您需要使用一个包含两者的实现
  • C++ 标准是否指定了编译器的 STL 实现细节?

    在写答案时this https stackoverflow com questions 30909296 can you put a pimpl class inside a vector我遇到了一个有趣的情况 这个问题演示了这样一种情况

随机推荐