字节到位运算符重载C++

Byte to bits Operator Overloading C++

本文关键字:重载 C++ 运算符 字节      更新时间:2023-10-16

我写C++已经很长时间了,也许是因为我不需要经常这样做,但我似乎在运算符重载方面有所欠缺。我不时地使用它,但从未需要做我最近想做的事情,并发现它有点问题。

class foo
{
public:
static const size_t ARRAY_SIZE = 100000;
uint8_t& operator[](const size_t& index) { return my_array[index >> 3]; }
// problematic equality operator
bool operator==(const size_t& index) const { return my_array[index >> 3] & (1 << (index & 7)); }
//
// Need an assignment operator to do:
// my_array[index >> 3] |= 1 << (index & 7);
// ^------------------^ might not needed as it's returned from [] operator
private:
std::array<uint8_t, (ARRAY_SIZE >> 3) + ((ARRAY_SIZE & 7) ? 1 : 0)> my_array;
};

从上面可以看出,这里要做的是获取一个size_t数字,并将其存储在它的相对位位置。因此,例如,5将存储在数组等中字节0的比特4中,9将存储在字节1的比特1中。

现在,下标运算符工作正常,并从数组中返回正确的字节,但这留下了这样的问题:

if (foo[n])    // where n is a size_t integer representing a bit position

然后我意识到,以上是的缩写形式

if (foo[n] == true)

因此,我编写了上面的等式运算符,但由于某种原因,我不明白,运算符没有被调用。我以为它会在下标运算符之后被调用,还是因为它不再是foo类型的对象而没有被调用?解决这个问题的最佳方法是什么?是写一个外部运算符==并使其成为foo的朋友吗?

哦,还有一些关于赋值运算符构造的建议也将不胜感激。非常感谢。。。

编辑:感谢所有人的帮助。我确实认为,因为问了一个我不太理解的问题而被否决是非常残酷的。这并不是一个愚蠢的问题,如果你仔细阅读我最初的问题,我确实质疑foo可能不是下标运算符后面的正确类型,正如你们中的一些人所指出的那样。无论如何,这里有更多的上下文。我还没有机会好好研究所有很棒的回复。。。

我最初写的操作符是这样的,它实际上从数组中返回了正确的位。有些人已经指出了。

bool operator[](const size_t index) const { return my_array[index >> 3] & (1 << (index & 7)); }

然后我遇到的问题是设置阵列中的位:

foo f;
if (f[3])  // this is fine

但做一些类似的事情:

f[6] = true;

我想我所希望的是一种比写以下内容更优雅的方式:-

class Foo
{
public:
static const size_t MAX_LIST_SIZE = 100000;
bool get(const size_t index) const { return my_array[index >> 3] & (1 << (index & 7)); }
void set(const size_t index) { my_array[index >> 3] |= 1 << (index & 7); }
private:
std::array<uint8_t, ((MAX_LIST_SIZE >> 3) + ((MAX_LIST_SIZE & 7) ? 1 : 0))> my_array;
}

然后使用这样的类:

Foo f
f.set(10);
if (f.get(10))
...

我只是觉得操作人员过载会更容易,但从外观上看,它似乎更麻烦。(哦,有人问我为什么用uint8_t而不是bool,这是因为在这个特定的平台上,bool实际上是32位!)

这里我们有几个深刻的误解。

现在下标运算符工作正常并返回正确的字节来自数组,但这留下了这样的问题:

if (foo[n])    // where n is a size_t integer representing a bit position

您的问题不是if本身;这是因为你归还的东西不对。如果您正在构建一个压缩比特集,那么operator[]应该只返回请求位置的比特值。因此:

bool operator[](size_t index) { return (my_array[index >> 3]) & (1<<(index&7)); }

在这里,您的if以及涉及operator[]的任何其他操作都将按预期工作。


然后我意识到上面是的缩写形式

if (foo[n] == true)

不是。if计算括号内的表达式,并(本质上)将其强制转换为布尔值;如果结果为true,则执行分支,否则不执行。

,因此我编写了上面的等式运算符,但由于某种原因我不理解,该运算符没有被调用。

未调用运算符,因为:

  1. 如上所述,operator==从不涉及if (foo[n])
  2. 即使显式编写了if (foo[n]==true),也不会调用您的运算符,因为一旦返回operator[],就不再涉及foo

想想看:即使在你的"原始的";operator[]返回对uint8_t的引用。声明:

if (a[n] == true)

(afoo型)

实际上与相同

uint8_t &temp = a[n];
if (temp == true)

现在,在表达式temp == true中,a的类型从未被提及——只有temp,它是uint8_t&,与它是如何获得的无关,还有true,它是一个bool文字。如果您将asize_t进行比较,则会考虑您的operator==,但这毫无意义。


最后,关于您的评论:

// Need an assignment operator to do:
// my_array[index >> 3] |= 1 << (index & 7);
// ^------------------^ might not needed as it's returned from [] operator

同样,由于完全相同的原因,这将不起作用-您需要一个运算符重载来处理operator[]的返回值,而不是foo类本身。

这通常是通过让operator[]返回的不是值本身,而是一个代理对象来实现的,该代理对象记住其父对象和请求的索引,并提供自己的operator==operator=来执行您试图在foo类中直接输入的内容(以及使其可以传递对布尔值的引用的额外运算符)。

类似于:

struct PackedBitVector {
static const size_t ARRAY_SIZE = 100000;
struct ElementProxy {
PackedBitVector &parent;
size_t idx;
operator bool() const { return parent.data[idx>>3] & (1<<(idx&7)) }
bool operator==(bool other) const { return bool(*this) == other; }
bool operator!=(bool other) const { return !(*this == other); }
ElementProxy &operator=(bool other) {
if(other) parent.data[idx>>3] |= 1<<(idx&7);
else      parent.data[idx>>3] &= ~(1<<(idx&7));
return *this;
}
}:
ElementProxy operator[](size_t index) { return ElementProxy{*this, index}; }
private:
std::array<uint8_t, (ARRAY_SIZE >> 3) + ((ARRAY_SIZE & 7) ? 1 : 0)> data;
};

要使其正常工作,您必须添加一个完整的其他操作符桶,以便该代理对象可以可靠地作为对bool的引用传递,std::vector<bool>就是这样做的。

关于这一点,从你关于bool在你的平台上是32位宽的评论中,你似乎不知道std::vector<bool>已经在运动了;压缩位阵列";空间优化,因此您可以直接使用它,而无需重新实现真实事物的损坏版本。