这是一个安全的方式来实现一个通用的运算符==和运算符<?
看到这个问题后,我首先想到的是定义泛型等价关系运算符是微不足道的:
#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 ++如何生成复制构造函数并不完全一致,例如,它执行成员复制。
但我想知道上述实施是否确实安全。 这些结构自然会有相同的包装,属于同一类型,但是填充的内容保证是相同的(例如,用零填充)? 有没有什么原因会导致这种情况无效?
除非你100%确定内存布局,编译器行为,并且你真的不关心可移植性,并且你真的想要获得效率
没有 - 例如,如果你有T ==(float | double | long double),你的operator==
不能正常工作。 即使两个NaN的位模式相同,实际上,检测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
指针的值,而不是它指向的值,这是您真正想要/需要的值。
编辑:就填充而言,不要求填充内容相同。 从理论上讲,填充可能是某种形式的陷阱表示,如果你甚至试图去查看它,它会导致信号,抛出异常,或者按照该顺序。 为了避免这种陷阱表示,你需要使用类似cast的东西来看它作为unsigned char
的缓冲区。 memcmp
可能会这样做,但它可能不会......
还要注意,是相同类型的对象并不一定意味着使用相同的成员对齐。 这是一种常见的实现方法,但编译器完全可以使用不同的对齐方式,例如基于特定对象“使用”的频率,并在对象中包含某种标记(例如,一个写入第一个填充字节的值),它告诉这个特定实例的对齐方式。 同样,它可以通过(例如)地址隔离对象,因此位于偶数地址的对象具有2字节的对齐方式,在4的倍数处具有4字节对齐方式的地址,等等(这不可能是用于POD类型,但除此之外,所有投注都关闭)。
这些都不是可能的或常见的,但我不能想到标准中禁止它们的任何东西。
即使是POD,==运算符也可能是错误的。 这是由于像我的编译器需要8个字节的结构对齐。
class Foo {
char foo; /// three bytes between foo and bar
int bar;
};
链接地址: http://www.djcxy.com/p/73767.html
上一篇: Is this a safe way to implement a generic operator== and operator<?