Байт в биты Перегрузка оператора C ++ - PullRequest
0 голосов
/ 26 октября 2018

Я давно пишу на 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 будет храниться в бите 4 байта 0, а 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бит!)

1 Ответ

0 голосов
/ 26 октября 2018

Здесь у нас есть несколько недоразумений с глубоким искажением.

Теперь оператор индекса работает нормально и возвращает правильный байт из массива, но это оставляет проблему таких вещей:

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

Ваша проблема здесь не в if per se ;это то, что вы возвращаете не ту вещь.Если вы строите упакованный битовый набор, ваш 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)

a типа foo)

фактически совпадает с:

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

Теперь в выраженииtemp == true тип a никогда не упоминается - есть только temp, который является uint8_t&, независимо от того, как он был получен, и true, bool литерал.Ваш operator== будет считаться, если вы сравниваете a с size_t, но это не имеет смысла.


Наконец, о вашем комментарии:

    // 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> уже поддерживает оптимизацию пространства "упакованного битового массива", так что вы можете использовать его напрямую,без переопределения сломанной версии реальной вещи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...