首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >memcmp与多个等式比较

memcmp与多个等式比较
EN

Stack Overflow用户
提问于 2015-03-04 15:31:22
回答 6查看 6.6K关注 0票数 24

前提条件:考虑这样一个类或结构T,对于类型为T的两个对象:ab

代码语言:javascript
复制
memcmp(&a, &b, sizeof(T)) == 0

产生的结果与

代码语言:javascript
复制
a.member1 == b.member1 && a.member2 == b.member2 && ...

(memberNT的一个非静态成员变量)。

问题:什么时候应该使用memcmp来比较ab的相等性,以及什么时候应该使用链接的==

下面是一个简单的例子:

代码语言:javascript
复制
struct vector
{
    int x, y;
};

要为vector重载操作符vector,有两种可能(如果它们保证提供相同的结果):

代码语言:javascript
复制
bool operator==(vector lhs, vector rhs)
{ return lhs.x == rhs.x && lhs.y == rhs.y; }

代码语言:javascript
复制
bool operator==(vector lhs, vector rhs)
{ return memcmp(&lhs, &rhs, sizeof(vector)) == 0; }

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

  • 如果使用==s实现operator==,则必须对其进行修改。
  • 如果使用memcmp,则根本不必修改operator==

但我认为使用链式==可以传达更清晰的含义。虽然对于一个拥有许多成员的大型T来说,memcmp更有吸引力。此外,在==上使用==是否会提高性能?还有什么要考虑的吗?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2015-03-04 17:48:55

对于memcmp产生与==的成员级比较结果相同的前提条件,虽然这一前提条件在实践中经常得到满足,但它有些脆弱。

理论上,改变编译器或编译器选项可以打破这一先决条件。更值得关注的是,代码维护(所有编程工作的80%是维护,IIRC)可以通过添加或删除成员、使类多态、添加自定义==重载等方式来破坏它。正如其中一个注释所提到的,前提条件对于静态变量可以保持不变,而对于自动变量则不成立,然后创建非静态对象的维护工作可能会对™造成不良影响。

关于是使用memcmp还是使用成员级==为类实现==操作符的问题,首先,这是一个错误的二分法,因为这些不是唯一的选项。

例如,从compare函数的角度来看,使用关系运算符重载的自动生成可以减少工作量和可维护性。std::string::compare函数就是这样一个函数的例子。

其次,要选择什么样的实现方案的答案在很大程度上取决于您认为什么是重要的,例如:

  • 如果一个人寻求最大限度地提高运行时效率,或者
  • 如果一个人试图创建最清晰的代码,或者
  • 如果一个人寻找最简洁的,最快的编写代码,或
  • 如果要使类最安全地使用,或者
  • 也许还有别的事?

生成关系运算符。

您可能听说过CRTP,这种奇怪的反复出现的模板模式。我记得,它是为了处理生成关系运算符重载的需求而发明的。不过,我可能会把它和别的东西混为一谈,但无论如何:

代码语言:javascript
复制
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==

代码语言:javascript
复制
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实现的类;我认为您会同意这段代码的扩展性更好,并且更简单:

代码语言:javascript
复制
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:按成员分列的比较成员。

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

代码语言:javascript
复制
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的关系运算符。

这是一种逆转事物的自然顺序的实现,它以<==的形式实现了<==,它们直接提供并以std::tuple比较的方式实现(使用std::tie)。

代码语言:javascript
复制
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操作符生成方案,该方案根据<=实现其他运算符(可能效率不高)。

实现E:通过手工使用<==进行比较。

这个实现的结果是没有应用任何抽象,只是敲开键盘,直接写机器应该做的事情:

代码语言:javascript
复制
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作为一般情况下的实现也是不安全的,但是对于==!=来说可能会做得很好。为了提高效率,您应该使用相关的编译器选项和环境更好地度量,并记住Donald的格言:“过早优化是万恶之源”(也就是说,花时间在这方面可能会适得其反)。

票数 17
EN

Stack Overflow用户

发布于 2015-03-04 15:38:28

如您所述,如果您选择了这样的类型,使这两个解决方案产生相同的结果(想必,您没有间接数据,并且对齐/填充都是相同的),那么显然您可以使用任何您喜欢的解决方案。

需要考虑的事项:

  1. 性能:--我怀疑你会看到多大的差异,但要确定,如果你在乎的话;
  2. 安全:--您说这两种解决方案对于您的T是一样的,但它们是吗?他们真的是吗?在所有系统上?您的memcmp方法可移植吗?可能不会;
  3. 清晰度:,如果你的前提条件改变了,而你没有做充分的评论--描述你的memcmp使用情况,那么你的程序就会崩溃--因此你使它变得脆弱;
  4. Consistency:可能在其他地方使用==;当然,对于不满足先决条件的每个T,您都必须这样做;除非这是对T的刻意优化,否则您可以考虑在整个程序中坚持一种单一的方法;
  5. 易用性:当然,很容易从链式==中遗漏一个成员,特别是当您的成员列表不断增加的时候。
票数 12
EN

Stack Overflow用户

发布于 2015-03-04 15:40:25

如果两种解决方案都是正确的,则更倾向于更具可读性的解决方案。我认为对于C++程序员来说,==memcmp更具可读性。我甚至可以使用std::tie而不是链接:

代码语言:javascript
复制
bool operator==(const vector &lhs, const vector &rhs)
{ return std::tie(lhs.x, lhs.y) == std::tie(rhs.x, rhs.y); }
票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28858359

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档