前提条件:考虑这样一个类或结构T,对于类型为T的两个对象:a和b。
memcmp(&a, &b, sizeof(T)) == 0产生的结果与
a.member1 == b.member1 && a.member2 == b.member2 && ...(memberN是T的一个非静态成员变量)。
问题:什么时候应该使用memcmp来比较a和b的相等性,以及什么时候应该使用链接的==?
下面是一个简单的例子:
struct vector
{
int x, y;
};要为vector重载操作符vector,有两种可能(如果它们保证提供相同的结果):
bool operator==(vector lhs, vector rhs)
{ return lhs.x == rhs.x && lhs.y == rhs.y; }或
bool operator==(vector lhs, vector rhs)
{ return memcmp(&lhs, &rhs, sizeof(vector)) == 0; }现在,如果要向vector添加一个新成员,例如,一个z组件:
==s实现operator==,则必须对其进行修改。memcmp,则根本不必修改operator==。但我认为使用链式==可以传达更清晰的含义。虽然对于一个拥有许多成员的大型T来说,memcmp更有吸引力。此外,在==上使用==是否会提高性能?还有什么要考虑的吗?
发布于 2015-03-04 17:48:55
对于memcmp产生与==的成员级比较结果相同的前提条件,虽然这一前提条件在实践中经常得到满足,但它有些脆弱。
理论上,改变编译器或编译器选项可以打破这一先决条件。更值得关注的是,代码维护(所有编程工作的80%是维护,IIRC)可以通过添加或删除成员、使类多态、添加自定义==重载等方式来破坏它。正如其中一个注释所提到的,前提条件对于静态变量可以保持不变,而对于自动变量则不成立,然后创建非静态对象的维护工作可能会对™造成不良影响。
关于是使用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; }
};在以上支持下,我们可以对您的问题进行调查。
实现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 )
{}
};实现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的关系运算符。
这是一种逆转事物的自然顺序的实现,它以<和==的形式实现了<和==,它们直接提供并以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操作符生成方案,该方案根据<和=实现其他运算符(可能效率不高)。
实现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 )
{}
};选择什么。
考虑到最有价值的可能方面的清单,如安全、清晰、高效、短小,评估以上每一种方法。
然后选择一种对你来说显然是最好的方法,或者一种看起来同样最好的方法。
指导原则:为了安全起见,您不希望选择方法A,减法,因为它依赖于关于值的假设。请注意,选项B memcmp作为一般情况下的实现也是不安全的,但是对于==和!=来说可能会做得很好。为了提高效率,您应该使用相关的编译器选项和环境更好地度量,并记住Donald的格言:“过早优化是万恶之源”(也就是说,花时间在这方面可能会适得其反)。
发布于 2015-03-04 15:38:28
如您所述,如果您选择了这样的类型,使这两个解决方案产生相同的结果(想必,您没有间接数据,并且对齐/填充都是相同的),那么显然您可以使用任何您喜欢的解决方案。
需要考虑的事项:
T是一样的,但它们是吗?他们真的是吗?在所有系统上?您的memcmp方法可移植吗?可能不会;memcmp使用情况,那么你的程序就会崩溃--因此你使它变得脆弱;==;当然,对于不满足先决条件的每个T,您都必须这样做;除非这是对T的刻意优化,否则您可以考虑在整个程序中坚持一种单一的方法;==中遗漏一个成员,特别是当您的成员列表不断增加的时候。发布于 2015-03-04 15:40:25
如果两种解决方案都是正确的,则更倾向于更具可读性的解决方案。我认为对于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); }https://stackoverflow.com/questions/28858359
复制相似问题