当两者都给出相同的结果时,我应该使用memcmp或链式等于操作吗?

eml*_*lai 23 c++ memcmp

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

memcmp(&a, &b, sizeof(T)) == 0
Run Code Online (Sandbox Code Playgroud)

产生与...相同的结果

a.member1 == b.member1 && a.member2 == b.member2 && ...
Run Code Online (Sandbox Code Playgroud)

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

问题:何时应该memcmp用于比较ab平等,何时应该使用链式==


这是一个简单的例子:

struct vector
{
    int x, y;
};
Run Code Online (Sandbox Code Playgroud)

超载运营商==vector,有两种可能性(如果他们保证给相同的结果):

bool operator==(vector lhs, vector rhs)
{ return lhs.x == rhs.x && lhs.y == rhs.y; }
Run Code Online (Sandbox Code Playgroud)

要么

bool operator==(vector lhs, vector rhs)
{ return memcmp(&lhs, &rhs, sizeof(vector)) == 0; }
Run Code Online (Sandbox Code Playgroud)

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

  • 如果==s用于实现operator==,则必须进行修改.
  • 如果memcmp使用,则operator==根本不需要修改.

但我认为使用链式==传达了更明确的含义.虽然对于T很多会员memcmp来说很有诱惑力.另外,有没有从使用性能改进memcmp==S' 还有什么需要考虑的吗?

Che*_*Alf 16

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

改变编译器或编译器选项理论上可以打破这个前提条件.更值得关注的是,代码维护(以及所有编程工作的80%是维护,IIRC)可以通过添加或删除成员,使类具有多态性,添加自定义==重载等来打破它.正如其中一条评论所述,前提条件如果静态变量不适用于自动变量,则可以保留静态变量,然后创建非静态对象的维护工作可以执行Bad Things™.

关于是否使用memcmp或成员方式为班级==实施==操作员的问题,首先,这是一个错误的二分法,因为那些不是唯一的选择.

例如,就功能而言,使用关系运算符重载的自动生成可以减少工作量并且更易于维护compare.该std::string::compare功能是这种功能的一个例子.

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

  • 应该寻求最大化运行时效率,或者

  • 应该寻求创建最清晰的代码,或者

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

  • 是否应该设法让班级最安全,或者

  • 还有别的吗?

生成关系运算符.

您可能听说过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; }
};
Run Code Online (Sandbox Code Playgroud)

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

实现A:通过减法进行比较.

这是一个提供全套关系运算符的类,不使用memcmp或者==:

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 )
    {}
};
Run Code Online (Sandbox Code Playgroud)

实施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 )
    {}
};
Run Code Online (Sandbox Code Playgroud)

实现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 )
    {}
};
Run Code Online (Sandbox Code Playgroud)

实现D:compare关系运营商.

这是一个实现某种逆转事物的自然秩序,通过实施的compare来讲<==,这是直接提供和实施方面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 )
    {}
};
Run Code Online (Sandbox Code Playgroud)

如上所述,客户端代码使用例如>需要a using namespace std::rel_ops;.

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

实施E:通过手动使用<和比较==.

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

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 )
    {}
};
Run Code Online (Sandbox Code Playgroud)

选择什么

考虑到最重要的可能方面的列表,如安全性,清晰度,效率,简洁性,评估上述每种方法.

然后选择对你来说最明显的那个,或者看起来同样最好的方法之一.

指导:为了安全起见,您不希望选择方法A,减法,因为它依赖于关于值的假设.请注意,选项B,memcmp作为一般情况的实现是不安全的,但是对于just ==!=.为了提高效率,您应该更好地测量,使用相关的编译器选项和环境,并记住Donald Knuth的格言:"过早优化是所有邪恶的根源"(即花费时间可能会适得其反).


Lig*_*ica 12

如果,正如你所说,你选择的类型使得两个解决方案产生相同的结果(可能,那么,你没有间接数据,并且对齐/填充是完全相同的),那么显然你可以使用你喜欢的任何解决方案.

需要考虑的事项:

  1. 表现:我怀疑你会看到多少差异,但如果你关心的,可以测量它以确定;
  2. 安全:嗯,你说两种解决方案对你来说都是一样的T,但是它们呢?他们真的吗?在所有系统上?你的memcmp方法便携吗?可能不是;
  3. 清晰度:如果你的前提条件发生了变化并且你没有充分评论 - 描述你的memcmp用法,那么你的程序可能会被破坏 - 因此你已经使它变得脆弱;
  4. 一致性:据推测你==在其他地方使用; 当然,你必须为每一个T不符合你的前提条件的人做这件事; 除非这是一个刻意的优化专业化T,否则您可以考虑在整个计划中坚持使用单一方法;
  5. 易用性:当然,很容易错过链接的成员==,特别是如果您的成员列表增长.


Rei*_*ica 6

如果两个解决方案都是正确的,则更喜欢更易读的解决方案 我会说,对于C++程序员来说,==它比可读性更强memcmp.我甚至会使用std::tie而不是链接:

bool operator==(const vector &lhs, const vector &rhs)
{ return std::tie(lhs.x, lhs.y) == std::tie(rhs.x, rhs.y); }
Run Code Online (Sandbox Code Playgroud)

  • 更好的是,将`tie`包装在一个小函数中,这样你就不必复制成员名称或排除任何内容:`return members(lhs)== members(rhs);` (4认同)