Как передать ссылку на объект не являющийся переменной?

Сделал небольшой класс BigInteger и возникли некоторые ошибки:

class BigInteger
{
private:
    string digits;
public:
    char operator[](const size_t& index) const
    {
        if (index >= this->digits.size())
            return '0';
        else
            return this->digits[index];
    }
    BigInteger operator+(const BigInteger& another)
    {
        size_t size = max(this->digits.size(), another.digits.size());
        int remainder = 0;
        for (size_t i = 0; i < size; i++)
        {
 /*====>*/  (*this)[i] = (*this)[i] + another[i] + remainder - '0';
            remainder = ((*this)[i] - '0') / 10;
 /*====>*/  (*this)[i] = ((*this)[i] - '0') % 10 + '0';
        }
        if (remainder == 1)
            this->digits.push_back('1');
        return *this;
    }
};

Ошибка в целом ясна: (*this)[i] = ... и в моей перезагрузке [] - т.к. я даю копию элемента, а не ссылку на него. Хорошо, надо тогда возвращаемый тип сделать ссылкой.

char& operator[](const size_t& index) const
    {
        if (index >= this->digits.size())
            return '0';
        else
            return this->digits[index];
    }

Всплывает ошибка: E0137: выражение должно быть допустимым для изменения левосторонним значением. Если сделал ссылку на элемент, то в const нельзя его менять? Я же саму ссылку не меняю?.. Хотя ссылка на элемент это и есть этот же элемент... я не уверен. Но сделаем тогда не const перезагрузку []:

char& operator[](const size_t& index)
    {
        if (index >= this->digits.size())
            return '0';
        else
            return this->digits[index];
    }

Всплывает другая ошибка: E0461: начальное значение ссылки на неконстантный параметр должно быть левосторонним значением. В какой-то степени тоже очевидная ошибка. Я возвращаю ссылку, но на что возвращать ссылку для `0'. Как можно исправить эти ошибки?


Ответы (1 шт):

Автор решения: Harry

Вы делаете неприятные вещи в смысле проектирования - возвращаете какое-то "левое" значение при выходе за границы. Это решение на грани фола, потому что вы не в состоянии определить, была ли попытка выхода за границы (что, вообще говоря, ошибка) или нет.

Но в принципе в этом ничего страшного, если пользоваться разумно. Теперь о решениях...

Если возвращать ссылку - можете просто завести дополнительный элемент char с нулем и возвращать ссылку на него, но это неприятно втройне - вы можете случайно его изменить, и вся логика рухнет.

Можно генерировать исключение при выходе за пределы, но это в принципе противоречит вашему намерению - возвращать 0 при выходе за границы.

Можно возвращать const char&, но тогда не будет работать ваше присваивание.

Но давайте теперь вы ответите на вопрос - а что, здесь

/*====>*/  (*this)[i] = (*this)[i] + another[i] + remainder - '0';
           remainder = ((*this)[i] - '0') / 10;
/*====>*/  (*this)[i] = ((*this)[i] - '0') % 10 + '0';

у вас может быть выход за границы диапазона? Если да (а это возможно в вашем коде) - то что вы хотите изменять? Какой-то левый 0 не пойми где?

Вам нужно расширить вашу строку заранее, чтобы в ней было size символов, и чтобы выход за границы стал невозможен. Иначе - еще раз - при строке в 3 символа куда вы будете писать, скажем, пятый, шестой, седьмой... - много символов? Для них надо подготовить место!

А когда подготовите - то зачем вообще этот вызов оператора? если куда проще

/*====>*/  digits[i] = digits[i] + another.digits[i] + remainder - '0';
           remainder = (digits[i] - '0') / 10;
/*====>*/  digits[i] = (digits[i] - '0') % 10 + '0';

И тогда можно оставлять первый вариант оператора

char operator[](const size_t& index) const

который возвращает значение и не позволяет его менять "через голову" класса.

Еще один вариант - для оператора

char& operator[](const size_t& index) 

(да, const здесь уже не допустим, так как вы разрешаете менять внутреннее состояние объекта класса. И const size_t& index, по-моему, тоже излишество - size_t index справится даже лучше) при выходе за границу строки бегом расширять строку так, чтобы этот index приходился на реальный символ. Тоже вариант... Только вот если внешний код запомнит ссылку, а потом, когда вы измените строку и в памяти ее содержимое может оказаться перемещено на другое место - будут неприятности.

Так что я за то, чтоб возвращать char. Возвращая char&, вы разрушаете инкапсуляцию, выворачивая наружу "внутренности" класса, да еще и с возможной проблемой инвалидации ссылки во время работы.

P.S. Намек: куда быстрее будет работать класс c представлением чисел в системе счисления с основанием 2n. Или хотя бы с каким-то большим удобным для вывода, типа 109..

→ Ссылка