看到this question之后,我首先想到的是定义泛型对等和关系运算符很简单:
#include <cstring>
template<class T>
bool operator==(const T& a, const T& b) {
return std::memcmp(&a, &b, sizeof(T)) == 0;
}
template<class T>
bool operator<(const T& a, const T& b) {
return std::memcmp(&a, &b, sizeof(T)) < 0;
}
using namespace std::rel_ops
随后将变得更加有用,因为运算符==
和<
的默认实现将使其完全通用。显然,这并不执行成员比较,而是按位比较,就好像该类型仅包含POD成员。这与C ++生成副本构造函数的方式并不完全一致,例如,do执行成员级复制。
但是我不知道上面的实现是否确实安全。这些结构自然会具有相同的填充,相同的类型,但是填充内容是否保证相同(例如,填充零)?有什么原因或在这种情况下不起作用吗?
除非您对内存布局,编译器行为有100%的把握,并且您真的不在乎可移植性,并且您确实想获得效率,否则请不要这样做
否-例如,如果您有T ==(float | double | long double),则您的operator==
无法正常工作。即使两个NaN具有相同的位模式,也绝不能将它们进行比较(实际上,检测NaN的一种常用方法是将数字与其自身进行比较-如果它不等于自身,则为NaN)。同样,两个浮点数(其指数中的所有位都设置为0)的值(精确地)为0.0,无论有效位中可能设置/清除了哪些位。]
您的operator<
正常工作的机会更少。例如,考虑std::string
的典型实现,如下所示:
template <class charT>
class string {
charT *data;
size_t length;
size_t buffer_size;
public:
// ...
};
[按照成员的顺序,您的operator<
将根据字符串恰好存储了数据的缓冲区的地址进行比较。例如,如果碰巧是首先由length
成员编写的,则您的比较将使用字符串的长度作为主键。无论如何,它不会根据实际的字符串内容进行比较,因为它只会查看data
指针的值,而不是它指向的指针,这正是您真正想要的/需要。
编辑:就填充而言,不要求填充的内容相等。从理论上讲,填充也可能是某种陷阱表示,它将导致信号,引发异常或按此顺序发生的任何事情,即使您甚至尝试查看它也是如此。为避免此类陷阱表示,您需要使用类似强制转换的方法将其视为unsigned char
的缓冲区。 memcmp
可能会这样做,但随后可能不会...
还请注意,相同类型的对象确实不是必然意味着使用相同的成员对齐方式。这是一种常见的实现方法,但是编译器也完全有可能根据其“认为”特定对象将被使用的频率进行不同的对齐,例如使用不同的对齐方式,并包括某种类型的[
class Foo {
char foo; /// three bytes between foo and bar
int bar;
};
==
和<
。有一天,它会咬你。
例如如果您要在类中比较的任何成员都是浮点数。
上述实现可能会将两个双精度数视为不相等,即使它们来自具有相同输入的相同数学计算-因为它们可能未生成完全相同的输出-而是两个非常相似的数字。
通常,此类数字应与适当的公差进行数字比较。
简短的答案:如果这是一个聪明的主意,那么该语言将具有默认的复制构造函数/赋值运算符。